Etude de cas sur l’analyse et la mesure de performance des Flux de Patients

Introduction

Ce travail porte sur l’analyse des flux de patients sur un plateau mutualisé de consultations externes d’un hôpital. L’objectif est de réaliser un diagnostic objectif de la performance organisationnelle du service d’urologie en s’appuyant sur les méthodes et outils d’analyse de flux vus précédemment ainsi que sur le langage R à travers l’environnement de RStudio. La figure ci-dessous illustre les principaux flux ainsi que le Plan du plateau de consultations (voir fichiers PlanConsultations.pdf et ZoomURO.pdf consultables à partir http://bit.ly/PlansCHUTlse)

Plan du plateau de consultations Afin de collecter des données sur les parcours suivis, les patients qui se sont présentés le 12/11/2015 ont été équipés d’une étiquette électronique (type RFID) qui a permis de tracer leurs parcours dans le plateau de consultation. Les données collectées ont été fusionnées avec les données des outils de gestion des dossiers administratifs et médicaux utilisés par les personnels. L’ensemble est disponible sous la forme d’un fichier log, illustré par le tableau suivant (voir annexe LogPatientUROseul_12112015.xlsx consultable à partir http://bit.ly/logPatients).

Vue du tableau de données de log patient
Vue du tableau de données de log patient

Les différentes de ce tableau de données sont :

  • ID (Col A) : Identifiant du patient
  • Timestamp start (Col B) : horodatage entrée de zone ou salle
  • Timestamp end (Col C) : horodatage sortie de zone ou salle
  • Activity_MACRO (Col D) : Activité suivie et indice salle (i)
  • Activity_DETAILS (Col E) : Type d’activité
  • Ress.Humaines (Col F) : Ressources humaines administratives ou soignantes intervenant dans l’activité
  • distance parcourues (Col G) : Distance parcourue cumulée
  • début/fin opX (Col H à O) : horodatage début/fin de chaque opération de prise en charge par une ressources administrative ou soignante (maxi 4 opérations par activité).

Devoir 2 - Travail demandé : Modélisation data-driven du service d’urologie

Ce travail est à rendre pour le 11/02/2026 (avant le cours) au format Rmd ou R (+pdf si besoin) avec pour titre “NOM_Prenom_FIE5-2-IOS-4-Devoir-2.*”

Ce deuxième devoir se place en suite directe du premier et vous propose un premier type de modélisation data-driven (et top down), c’est-à-dire basé sur les travaux de Whitt & Zhang (2017) A data-driven model of an emergency department.

Question 1) Loi de Little (/3)

Pour rappel, la loi de Little est une loi fondamentale qui, dans un cadre asymptotique, lie le niveau d’occupation moyen au temps d’attente moyen par le taux d’arrivée moyen selon la formule donnée ci-dessous : \[ L = \lambda W \] - \(L\) : Niveau d’occupation moyen (asymptotique) - \(\lambda\) : Taux d’arrivée moyen (asymptotique) - \(W\) : Temps d’attente moyen (asymptotique)

Cette loi s’applique quel que soit le système ou modèle considéré à partir du moment où \(L<+\infty\), \(W<+\infty\) et \(\lambda<+\infty\) (Little JDC, Graves SC. Little’s law. In: International series in operations research and management science. 2008: 81–100.)

Pour cette première question, il vous est demandé de vérifier que cette loi “s’applique” bien sur notre service d’urologie durant la période de 8h à 18h de la journée du 12/11/2015.

Réponse :

Attention : utiliser bien les **arrivées initiales** des patients et pas les arrivées intermédiaires après une transition
  • Calcul le niveau d’occupation moyen :

    Rappel et conseil : \(L = \frac{1}{\int dt} \int l(t)dt\) avec \(l(t) = A(t) - D(t)\), vous pouvez calculer en décomposant la période en zone \(i\) de même niveau d’occupation et en calculant \(L[8h;18h] = \frac{1}{\sum_i t_i} \sum_i l_i t_i\) avec \(t_i\) la durée de la zone \(i\)

library(readxl)
library(dplyr)

# Calcul de L
# Load the Excel dataset Patient URO
df <- read_excel("Log_Patient_URO_12112015.xlsx")

# Rename columns for easier handling
df1 <- df %>%
  rename(
    Ress_Humaines = `Ress. Humaines`,
    Timestamp_start = `Timestamp start`,
    Timestamp_end   = `Timestamp end`,
    DISTANCE_PARCOURUE = `distance parcourue`
  ) %>%
  mutate(
    Timestamp_start = as.POSIXct(Timestamp_start,
                                 format = "%d/%m/%Y %H:%M:%S",
                                 tz = "Europe/Paris")
  )

# Get time in system based on entry and exit
temps_systeme <- df1 %>%
  group_by(ID) %>%
  summarise(
    entree = min(Timestamp_start, na.rm = TRUE),
    sortie = max(Timestamp_start,   na.rm = TRUE)
  ) %>%
  ungroup()

events_arr <- temps_systeme %>%
  dplyr::select(time = entree) %>%
  mutate(delta = 1)

events_dep <- temps_systeme %>%
  dplyr::select(time = sortie) %>%
  mutate(delta = -1)

events <- bind_rows(events_arr, events_dep) %>%
  arrange(time)

events <- events %>%
  mutate(
    L_t = cumsum(delta),
    dt = as.numeric(difftime(lead(time), time, units = "hours"))
  )

L <- sum(events$L_t * events$dt, na.rm = TRUE) /
     sum(events$dt, na.rm = TRUE)

L
[1] 9.134184
  • Calcul du taux d’arrivée moyen :
library(dplyr)
library(readxl)
library(tidyverse)
# Calcul de lamba
# Load the Excel dataset Patient URO
df <- read_excel("Log_Patient_URO_12112015.xlsx")
df

# Rename columns for easier handling
df1 <- df %>%
   rename(Ress_Humaines = `Ress. Humaines`,
        Timestamp_start = `Timestamp start`,
        Timestamp_end   = `Timestamp end`,
        DISTANCE_PARCOURUE = `distance parcourue`)

arrivees <- df1 %>%
  filter(Activity_MACRO == "Entrée des Consultations") %>%
  group_by(ID) %>%
  summarise(arrivee = min(Timestamp_start)) %>%
  ungroup()

arrivees

arrivees_8_18 <- arrivees %>%
  filter(format(arrivee, "%H:%M:%S") >= "08:00:00",
         format(arrivee, "%H:%M:%S") <= "18:00:00")

arrivees_8_18

lambda <- nrow(arrivees_8_18) / 10
lambda
[1] 6.8
  • Calcul du niveau d’attente moyen :
# Load the Excel dataset Patient URO
df <- read_excel("Log_Patient_URO_12112015.xlsx")
df

# Rename columns for easier handling
df1 <- df %>%
   rename(Ress_Humaines = `Ress. Humaines`,
        Timestamp_start = `Timestamp start`,
        Timestamp_end   = `Timestamp end`,
        DISTANCE_PARCOURUE = `distance parcourue`)

# Get time in system based on entry and exit
temps_systeme <- df1 %>%
  group_by(ID) %>%
  summarise(
    entree = min(Timestamp_start, na.rm = TRUE),
    sortie = max(Timestamp_start, na.rm = TRUE)
  ) %>%
  ungroup()

temps_systeme

temps_systeme <- temps_systeme %>%
  mutate(W = as.numeric(difftime(sortie, entree, units = "hours")))

W <- mean(temps_systeme$W, na.rm = TRUE)
W
[1] 1.772818
  • Comparaison de \(L\) et \(\lambda W\) :
```r
library(dplyr)
library(readxl)

# OCCUPATION
events <- bind_rows(
  temps_systeme %>% transmute(time = entree, delta = 1),
  temps_systeme %>% transmute(time = sortie, delta = -1)
) %>%
  arrange(time) %>%
  mutate(
    L_t = cumsum(delta),
    dt = as.numeric(difftime(lead(time), time, units = "hours"))
  )

L <- sum(events$L_t * events$dt, na.rm = TRUE) /
     sum(events$dt, na.rm = TRUE)

L 
```
```
[1] 9.134184
```
```r
lambda*W
```
```
[1] 12.05516
```
  • Rappel et conseil : Tenter d’expliquer pourquoi on observe une différence. En particulier, pour cette explication penser à pourquoi \(\lambda W\) peut être assimilée à une moyenne des niveaux de présence moyen de patients \(m\) : \(\frac{1}{M}\sum_{m \in M} \frac{1}{t}\int_0^t \mathbb{1}_m(t) dt\) avec \(\mathbb{1}_m(t)\) la fonction d’indicatrice de présence du patient \(m\)

Réponse :

On observe que L≈9 alors que λ*W≈12 La différence s’explique par le fait que :

  • Effets de bord temporels : La journée ne couvre pas un cycle complet (patients arrivés avant 8h ou partis après 18h)

  • Non-stationnarité : Les arrivées sont concentrées le matin, les départs étalés

  • Moyenne temporelle vs moyenne par patient : L est une moyenne dans le temps, λW est une moyenne sur les patients

Question 2) Processus de Poisson d’arrivée non homogène (/3)

Un processus de poisson est un processus de comptage (dans le temps) indiquant un nombre évènements ayant occurés entre un temps \(0\) et un temps \(t\) selon une distribution de Poisson \(\mathcal(P)(\lambda * t)\) avec un taux par unité de temps \(\lambda\). Nous verrons dans le cours 3 que les temps entre chaque événement suive une loi de distribution exponentielle de paramètre \(\lambda\).

Un processus de Poisson non homogène (NHPP) est un processus de comptage où le taux d’événement \(\lambda(t)\) n’est pas constant. En considérant une modélisation de NHPP basé sur taux d’arrivées des tranches [8h;10h], ]10h;12h], …, ]16h;18h] du service d’urologie générer 30 échantillons de ce NHPP sur la période 8h, 18h et illustrés les.

Pour vous aider voici un exemple pour un Processus de Poisson homogène : (n’hésitez pas à aller plus loin aussi pour l’illustration)

lambda = 10 # par heure
samples <- tibble(run=to_vec(for(i in 1:10) rep(i,200)),
       id=rep(1:200,10),
       delta_t=rexp(2000,lambda)) %>%
  group_by(run) %>%
  mutate(t = cumsum(delta_t)) %>%
  filter(t <= 10)

# illustration 1
samples %>% 
  ggplot(aes(t,id,color=factor(run))) +
  geom_point() 


# illustration 2
samples %>%   
  mutate(t = cut(t,seq(0,30,by=1),include.lowest = TRUE)) %>%
  count(run,t) %>%
  arrange(run,t) %>%
  ggplot(aes(t,n)) +
  geom_boxplot()

Attention : pour un processus de Poisson non homogène, il faudra générer le nombre d'arrivées avec _rpoiss_ pour chaque tranche de temps avec un taux différent et ensuite les répartir uniformément dans le temps (au sein de leur intervalle) en générant des valuers uniform (_runif_).

Réponse :


# Import des données
data_raw <- read_excel("Log_Patient_URO_12112015.xlsx")

# on garde que l'arrivée initiale
data_arrivals <- data_raw %>%
  mutate(arrival_time = ymd_hms(`Timestamp start`)) %>%
  group_by(ID) %>%                
  slice_min(arrival_time, n = 1) %>%        
  ungroup() %>%
  mutate(
    hour = hour(arrival_time) + minute(arrival_time)/60
  ) %>%
  filter(hour >= 8, hour < 18)

# Découpage en tranches horaires
data_arrivals <- data_arrivals %>%
  mutate(
    tranche = cut(
      hour,
      breaks = c(8,10,12,14,16,18),
      include.lowest = TRUE,
      right = TRUE
    )
  )

# Estimation des taux λ(t)
lambda_hat <- data_arrivals %>%
  count(tranche) %>%
  mutate(lambda = n / 2) 

# Définition des intervalles
intervals <- tibble(
  start  = c(8,10,12,14,16),
  end    = c(10,12,14,16,18),
  lambda = lambda_hat$lambda
)

# Simulation du NHPP (30 runs)
set.seed(123)
n_runs <- 30

samples_nhpp <- map_dfr(1:n_runs, function(run_id) {

  map_dfr(1:nrow(intervals), function(i) {

    dt <- intervals$end[i] - intervals$start[i]
    n_events <- rpois(1, intervals$lambda[i] * dt)

    tibble(
      run = run_id,
      t = runif(n_events, intervals$start[i], intervals$end[i])
    )
  })
}) %>%
  arrange(run, t) %>%
  group_by(run) %>%
  mutate(id = row_number()) %>%
  ungroup()

# Illustration 1 : trajectoires
samples_nhpp %>%
  ggplot(aes(x = t, color = factor(run))) +
  stat_ecdf(geom = "step", alpha = 0.5) +
  scale_y_continuous(labels = scales::percent) +
  labs(
    title = "Trajectoires du NHPP – Fonction de comptage",
    x = "Temps (heures)",
    y = "Proportion d'arrivées cumulées"
  ) +
  theme_minimal() +
  theme(legend.position = "none")


# Calcul des arrivées réelles par heure
real_arrivals <- data_arrivals %>%
  mutate(hour = cut(hour, seq(8,18,by=1), include.lowest = TRUE)) %>%
  count(hour)

# Illustration 2 AMÉLIORÉE
samples_nhpp %>%
  mutate(hour = cut(t, seq(8,18,by=1), include.lowest = TRUE)) %>%
  count(run, hour) %>%
  ggplot(aes(hour, n)) +
  geom_boxplot(fill = "lightblue", alpha = 0.6) +
  geom_point(data = real_arrivals, aes(hour, n), 
             color = "red", size = 3, shape = 18) +  
  labs(
    title = "Distribution des arrivées par heure",
    subtitle = "Boxplot: NHPP simulé (30 runs) | Losanges rouges: données réelles",
    x = "Heure",
    y = "Nombre d'arrivées"
  ) +
  theme_minimal()

Question 3) Modélisation de la durée de séjour par une distribution (/3)

A l’aide de la documentation “Ricci-distributions-en.pdf” fournie, notamment avec la fonction fitdistr() de la librairie MASS, tester la modélisation (par MLE, Maximum Likelihood Estimation) la durée de séjours de l’ensemble des patients avec différentes distributions : normale, exponentielle, gamma, weibull.

Réponse :

Entrez votre texte ici

#_Entrez votre code R ici_

library(readxl)
library(MASS)

data_service <- read_excel("Log_Patient_URO_12112015.xlsx")

data_raw <- data_service %>%
  mutate(
    datetime_begin = ymd_hms(`Timestamp start`),
    datetime_end   = ymd_hms(`Timestamp end`)
  )

data_patient <- data_raw %>%
  group_by(ID) %>%
  summarise(
    arrival_time = min(datetime_begin, na.rm = TRUE),
    departure_time = max(datetime_end, na.rm = TRUE),
    .groups = "drop"
  )

data_patient <- data_patient %>%
  mutate(
    duree_sejour = as.numeric(difftime(departure_time,
                                       arrival_time,
                                       units = "hours"))
  )

duree <- data_patient$duree_sejour
duree <- duree[!is.na(duree)]
duree <- duree[duree > 0]

summary(duree)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.4654  0.7964  1.5256  1.7322  2.4347  5.8699 
hist(duree, breaks = 30)


fit_norm <- fitdistr(duree, "normal")
fit_norm
      mean          sd    
  1.73217947   1.12042754 
 (0.13587180) (0.09607587)
fit_exp <- fitdistr(duree, "exponential")
fit_exp
     rate   
  0.5773074 
 (0.0700088)
fit_gamma <- fitdistr(duree, "gamma")
fit_gamma
     shape       rate   
  2.6808643   1.5476811 
 (0.4342089) (0.2756369)
fit_weib <- fitdistr(duree, "weibull")
fit_weib
     shape       scale  
  1.6654502   1.9525277 
 (0.1507457) (0.1506968)
AIC_fitdistr <- function(fit) {
  k <- length(fit$estimate)
  -2 * fit$loglik + 2 * k
}

AIC_norm  <- AIC_fitdistr(fit_norm)
AIC_exp   <- AIC_fitdistr(fit_exp)
AIC_gamma <- AIC_fitdistr(fit_gamma)
AIC_weib  <- AIC_fitdistr(fit_weib)

AIC_values <- data.frame(
  Distribution = c("Normale", "Exponentielle", "Gamma", "Weibull"),
  AIC = c(AIC_norm, AIC_exp, AIC_gamma, AIC_weib)
)

AIC_values[order(AIC_values$AIC), ]

hist(duree, prob = TRUE, breaks = 30,
     main = "Ajustement des lois – Durée de séjour",
     xlab = "Durée")

curve(dnorm(x,
            mean = fit_norm$estimate[1],
            sd   = fit_norm$estimate[2]),
      add = TRUE, col = "blue", lwd = 2)

curve(dexp(x,
           rate = fit_exp$estimate),
      add = TRUE, col = "red", lwd = 2)

curve(dgamma(x,
             shape = fit_gamma$estimate["shape"],
             rate  = fit_gamma$estimate["rate"]),
      add = TRUE, col = "green", lwd = 2)

curve(dweibull(x,
               shape = fit_weib$estimate["shape"],
               scale = fit_weib$estimate["scale"]),
      add = TRUE, col = "purple", lwd = 2)

legend("topright",
       legend = c("Normale", "Exponentielle", "Gamma", "Weibull"),
       col = c("blue", "red", "green", "purple"),
       lwd = 2)

Quelles est la meilleure distribution ? (Justifiez) Comparez aussi les moyennes, écart-types et coefficients de variation obtenus pour chaque distribution et par rapport au calcul direct des indicateurs sur les variables statistiques.

Réponse :

Parmi les distributions étudiées, la loi de Weibull apparaît comme la plus appropriée pour modéliser la durée de séjour. Elle respecte le support strictement positif de la variable et reproduit correctement les principaux indicateurs statistiques, en particulier la variabilité observée. La loi Gamma aurait également pu constituer un choix pertinent, car elle présente des caractéristiques proches et un bon accord avec les données empiriques ; toutefois, la Weibull offre une flexibilité légèrement supérieure, notamment dans la modélisation des durées extrêmes et dans l’interprétation du comportement du taux de sortie au cours du séjour. La loi normale, bien que numériquement proche, reste conceptuellement inadaptée en raison de son support non borné inférieur, tandis que la loi exponentielle est clairement inappropriée car elle impose une variabilité trop élevée par rapport aux observations. Ainsi, la loi de Weibull est retenue comme meilleur compromis, la loi Gamma pouvant être considérée comme une alternative valable en second choix.

#_Entrez votre code R ici_

# Indicateurs empiriques
mean_emp <- mean(duree)
sd_emp   <- sd(duree)
cv_emp   <- sd_emp / mean_emp

mean_norm <- fit_norm$estimate["mean"]
sd_norm   <- fit_norm$estimate["sd"]
cv_norm   <- sd_norm / mean_norm

rate_exp <- fit_exp$estimate["rate"]

mean_exp <- 1 / rate_exp
sd_exp   <- 1 / rate_exp
cv_exp   <- sd_exp / mean_exp

shape_g <- fit_gamma$estimate["shape"]
rate_g  <- fit_gamma$estimate["rate"]

mean_gamma <- shape_g / rate_g
sd_gamma   <- sqrt(shape_g) / rate_g
cv_gamma   <- sd_gamma / mean_gamma

shape_w <- fit_weib$estimate["shape"]
scale_w <- fit_weib$estimate["scale"]

mean_weib <- scale_w * gamma(1 + 1 / shape_w)
sd_weib   <- scale_w * sqrt(
  gamma(1 + 2 / shape_w) - gamma(1 + 1 / shape_w)^2
)
cv_weib <- sd_weib / mean_weib

comparaison_table <- data.frame(
  Distribution = c("Empirique", "Normale", "Exponentielle", "Gamma", "Weibull"),
  Moyenne = c(mean_emp, mean_norm, mean_exp, mean_gamma, mean_weib),
  Ecart_type = c(sd_emp, sd_norm, sd_exp, sd_gamma, sd_weib),
  Coefficient_variation = c(cv_emp, cv_norm, cv_exp, cv_gamma, cv_weib)
)

comparaison_table
NA

Quel que soit votre réponse, refaites ce travail pour la distribution gamma en séparant la modélisation des durées de séjour des patients prioritaires et non prioritaires et comparez les deux distributions graphiquement et à l’aide leur moments (espérance, variance, coefficient de variation) et/ou leur paramètres d’échelle et de forme.

Réponse :

L’analyse des durées de séjour des patients selon une loi Gamma montre que, bien que la durée moyenne soit très proche entre les patients prioritaires (≈1,76) et non prioritaires (≈1,72), la dispersion diffère sensiblement. Les patients prioritaires présentent une variance plus élevée (1,66 contre 0,91) et un coefficient de variation plus important (0,73 contre 0,55), indiquant une plus grande variabilité relative de leurs durées de séjour. Cette différence se reflète également dans les paramètres de la loi Gamma : le paramètre shape des patients prioritaires (≈1,86) est plus faible que celui des non prioritaires (≈3,26), ce qui traduit une distribution plus asymétrique et étalée, avec une queue plus longue à droite. À l’inverse, la distribution des non prioritaires est plus concentrée autour de la moyenne. Ainsi, même si les durées moyennes sont similaires, les patients prioritaires montrent des séjours plus hétérogènes, ce qui peut avoir des implications pour la planification hospitalière et la gestion des ressources.

#_Entrez votre code R ici_

data_patient <- data_raw %>%
  mutate(
    datetime_begin = ymd_hms(`Timestamp start`),
    datetime_end   = ymd_hms(`Timestamp end`)
  ) %>%
  group_by(ID) %>%
  summarise(
    arrival_time = min(datetime_begin, na.rm = TRUE),
    departure_time = max(datetime_end, na.rm = TRUE),
    prioritaire = ifelse(any(grepl("PRIO", Activity_DETAILS)), "Oui", "Non"),
    .groups = "drop"
  ) %>%
  mutate(
    duree_sejour = as.numeric(difftime(departure_time,
                                       arrival_time,
                                       units = "hours"))
  )

duree_prio <- data_patient %>%
  filter(prioritaire == "Oui") %>%
  pull(duree_sejour)

duree_non_prio <- data_patient %>%
  filter(prioritaire == "Non") %>%
  pull(duree_sejour)

# Nettoyage
duree_prio <- duree_prio[duree_prio > 0 & !is.na(duree_prio)]
duree_non_prio <- duree_non_prio[duree_non_prio > 0 & !is.na(duree_non_prio)]

library(MASS)

fit_gamma_prio <- fitdistr(duree_prio, "gamma")
fit_gamma_non_prio <- fitdistr(duree_non_prio, "gamma")

params_gamma <- data.frame(
  Groupe = c("Prioritaires", "Non prioritaires"),
  Shape = c(fit_gamma_prio$estimate["shape"],
            fit_gamma_non_prio$estimate["shape"]),
  Rate = c(fit_gamma_prio$estimate["rate"],
           fit_gamma_non_prio$estimate["rate"])
)

params_gamma

moments_gamma <- data.frame(
  Groupe = c("Prioritaires", "Non prioritaires"),
  Esperance = c(
    fit_gamma_prio$estimate["shape"] / fit_gamma_prio$estimate["rate"],
    fit_gamma_non_prio$estimate["shape"] / fit_gamma_non_prio$estimate["rate"]
  ),
  Variance = c(
    fit_gamma_prio$estimate["shape"] / fit_gamma_prio$estimate["rate"]^2,
    fit_gamma_non_prio$estimate["shape"] / fit_gamma_non_prio$estimate["rate"]^2
  ),
  Coefficient_variation = c(
    1 / sqrt(fit_gamma_prio$estimate["shape"]),
    1 / sqrt(fit_gamma_non_prio$estimate["shape"])
  )
)

moments_gamma

hist(duree_prio, prob = TRUE, breaks = 30,
     col = rgb(1,0,0,0.35),
     xlim = range(c(duree_prio, duree_non_prio)),
     main = "Comparaison des durées de séjour – Loi Gamma",
     xlab = "Durée (heures)")

curve(dgamma(x,
             shape = fit_gamma_prio$estimate["shape"],
             rate  = fit_gamma_prio$estimate["rate"]),
      col = "red", lwd = 2, add = TRUE)

hist(duree_non_prio, prob = TRUE, breaks = 30,
     col = rgb(0,0,1,0.35),
     add = TRUE)

curve(dgamma(x,
             shape = fit_gamma_non_prio$estimate["shape"],
             rate  = fit_gamma_non_prio$estimate["rate"]),
      col = "blue", lwd = 2, add = TRUE)

legend("topright",
       legend = c("Prioritaires", "Non prioritaires"),
       col = c("red", "blue"),
       lwd = 2)

# Q-Q plot pour prioritaires
par(mfrow = c(1, 2))


# Prioritaires
qqplot(qgamma(ppoints(length(duree_prio)),
              shape = fit_gamma_prio$estimate["shape"],
              rate = fit_gamma_prio$estimate["rate"]),
       duree_prio,
       main = "Q-Q Plot - Prioritaires",
       xlab = "Quantiles théoriques (Gamma)",
       ylab = "Quantiles observés")
abline(0, 1, col = "red")

# Non-prioritaires
qqplot(qgamma(ppoints(length(duree_non_prio)),
              shape = fit_gamma_non_prio$estimate["shape"],
              rate = fit_gamma_non_prio$estimate["rate"]),
       duree_non_prio,
       main = "Q-Q Plot - Non prioritaires",
       xlab = "Quantiles théoriques (Gamma)",
       ylab = "Quantiles observés")
abline(0, 1, col = "blue")

par(mfrow = c(1, 1))

Question 4) Modélisation de la durée de séjour par régression linéaire (/3)

A l’aide de la fonction lm, construisez et analysez un modèle de régression linéaire estimant la durée de séjour qui prennent en variable d’entrée le bloc de 2h [8h;10h], …, ]16h;18h].

Voici un petit exemple d’utilisation :

Y = c(1, 1.5, 2, 3)
X = c("A","A","B","B")

model <- lm(Y ~ X)
summary(model) 

Call:
lm(formula = Y ~ X)

Residuals:
    1     2     3     4 
-0.25  0.25 -0.50  0.50 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)  
(Intercept)   1.2500     0.3953   3.162   0.0871 .
XB            1.2500     0.5590   2.236   0.1548  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.559 on 2 degrees of freedom
Multiple R-squared:  0.7143,    Adjusted R-squared:  0.5714 
F-statistic:     5 on 1 and 2 DF,  p-value: 0.1548
predict(model) # predict(model,newdata=[new_dataframe]) if new data
   1    2    3    4 
1.25 1.25 2.50 2.50 

Réponse :

Entrez votre texte ici


library(dplyr)
library(lubridate)
library(ggplot2)
library(readxl)
library(broom)  # ✅ IMPORTANT : Charger broom au début !


# Chargement des données
df <- read_excel("Log_Patient_URO_12112015.xlsx")

# Renommer les colonnes
df1 <- df %>%
  rename(
    Ress_Humaines = `Ress. Humaines`,
    Timestamp_start = `Timestamp start`,
    Timestamp_end   = `Timestamp end`,
    DISTANCE_PARCOURUE = `distance parcourue`
  ) %>%
  mutate(
    Timestamp_start = as.POSIXct(Timestamp_start, format = "%d/%m/%Y %H:%M:%S", tz = "Europe/Paris"),
    Timestamp_end   = as.POSIXct(Timestamp_end, format = "%d/%m/%Y %H:%M:%S", tz = "Europe/Paris")
  )

# Calculer la durée de séjour et l'heure d'arrivée pour chaque patient
temps_systeme <- df1 %>%
  group_by(ID) %>%
  summarise(
    entree = min(Timestamp_start, na.rm = TRUE),
    sortie = max(Timestamp_end, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(
    W = as.numeric(difftime(sortie, entree, units = "hours")),
    heure_entree = hour(entree) + minute(entree)/60
  )

# Créer la variable bloc_2h selon l'heure d'arrivée
temps_systeme <- temps_systeme %>%
  mutate(
    bloc_2h = case_when(
      heure_entree >= 8 & heure_entree < 10 ~ "[08h;10h]",
      heure_entree >= 10 & heure_entree < 12 ~ "[10h;12h]",
      heure_entree >= 12 & heure_entree < 14 ~ "[12h;14h]",
      heure_entree >= 14 & heure_entree < 16 ~ "[14h;16h]",
      heure_entree >= 16 & heure_entree < 18 ~ "[16h;18h]",
      TRUE ~ NA_character_
    )
  ) %>%
  filter(!is.na(bloc_2h), W > 0, !is.na(W))

temps_systeme <- temps_systeme %>%
  mutate(
    bloc_2h = factor(bloc_2h, 
                     levels = c("[08h;10h]", "[10h;12h]", "[12h;14h]", 
                                "[14h;16h]", "[16h;18h]"))
  )

# Vérification
head(temps_systeme)
summary(temps_systeme$W)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.4654  0.7963  1.5256  1.7189  2.4347  5.8699 
# ==============================================================================
# MODÈLE 1
# ==============================================================================

model1 <- lm(W ~ bloc_2h, data = temps_systeme)
summary(model1)

Call:
lm(formula = W ~ bloc_2h, data = temps_systeme)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.5650 -0.8471 -0.1567  0.7813  3.7957 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)  
(Intercept)        0.9368     0.4557   2.056   0.0443 *
bloc_2h[10h;12h]   0.6305     0.5196   1.213   0.2299  
bloc_2h[12h;14h]   0.7602     0.5765   1.319   0.1923  
bloc_2h[14h;16h]   1.1374     0.5582   2.038   0.0461 *
bloc_2h[16h;18h]   1.0119     0.5344   1.894   0.0632 .
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.116 on 59 degrees of freedom
Multiple R-squared:  0.08116,   Adjusted R-squared:  0.01887 
F-statistic: 1.303 on 4 and 59 DF,  p-value: 0.2795
# Prédictions
temps_systeme$pred_W1 <- predict(model1)

# Visualisation
ggplot(temps_systeme, aes(x = bloc_2h, y = W)) +
  geom_jitter(width = 0.2, height = 0, alpha = 0.5, color = "lightblue", size = 2) +
  geom_point(aes(y = pred_W1), color = "red", size = 4, shape = 17) +
  stat_summary(fun = mean, geom = "point", color = "darkgreen", size = 4, shape = 18) +
  labs(
    title = "Modèle 1 : Durée de séjour selon le bloc horaire",
    subtitle = "Triangles rouges = prédictions | Losanges verts = moyennes observées",
    x = "Bloc horaire d'arrivée",
    y = "Durée de séjour W (heures)"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    axis.text.x = element_text(angle = 0)
  )


# ✅ CORRECTION : Diagnostics UN PAR UN au lieu de 4 en même temps
plot(model1, which = 1, main = "Modèle 1 - Résidus vs Valeurs ajustées")

plot(model1, which = 2, main = "Modèle 1 - Q-Q plot")

plot(model1, which = 3, main = "Modèle 1 - Scale-Location")

plot(model1, which = 5, main = "Modèle 1 - Résidus vs Levier")


# ==============================================================================
# MODÈLE 2
# ==============================================================================

# Identifier les patients prioritaires
temps_systeme2 <- temps_systeme %>%
  left_join(
    df1 %>%
      group_by(ID) %>%
      summarise(prioritaire = ifelse(any(grepl("PRIO", Activity_DETAILS)), "Oui", "Non"),
                .groups = "drop"),
    by = "ID"
  )

temps_systeme2 <- temps_systeme2 %>%
  mutate(prioritaire = factor(prioritaire, levels = c("Non", "Oui")))

# Vérification
table(temps_systeme2$prioritaire)

Non Oui 
 45  19 
# Modèle 2
model2 <- lm(W ~ bloc_2h + prioritaire, data = temps_systeme2)
summary(model2)

Call:
lm(formula = W ~ bloc_2h + prioritaire, data = temps_systeme2)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.5567 -0.8390 -0.1515  0.7885  3.7790 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)  
(Intercept)       0.93268    0.46253   2.016   0.0484 *
bloc_2h[10h;12h]  0.62588    0.52714   1.187   0.2399  
bloc_2h[12h;14h]  0.75690    0.58286   1.299   0.1992  
bloc_2h[14h;16h]  1.13320    0.56530   2.005   0.0497 *
bloc_2h[16h;18h]  1.00986    0.53958   1.872   0.0663 .
prioritaireOui    0.02496    0.31047   0.080   0.9362  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.126 on 58 degrees of freedom
Multiple R-squared:  0.08126,   Adjusted R-squared:  0.002062 
F-statistic: 1.026 on 5 and 58 DF,  p-value: 0.4109
# Prédictions
temps_systeme2$pred_W2 <- predict(model2)

# Visualisation
ggplot(temps_systeme2, aes(x = bloc_2h, y = W, color = prioritaire)) +
  geom_jitter(width = 0.2, height = 0, alpha = 0.4, size = 2) +
  geom_point(aes(y = pred_W2), shape = 17, size = 4) +
  geom_line(aes(y = pred_W2, group = prioritaire), linewidth = 1.2) +
  labs(
    title = "Modèle 2 : W ~ bloc_2h + prioritaire",
    subtitle = "Triangles = prédictions | Lignes parallèles montrent l'effet additif",
    x = "Bloc horaire d'arrivée",
    y = "Durée de séjour W (heures)",
    color = "Prioritaire"
  ) +
  scale_color_manual(values = c("Non" = "steelblue", "Oui" = "tomato")) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    legend.position = "top"
  )


# Diagnostics UN PAR UN
plot(model2, which = 1, main = "Modèle 2 - Résidus vs Valeurs ajustées")

plot(model2, which = 2, main = "Modèle 2 - Q-Q plot")

plot(model2, which = 3, main = "Modèle 2 - Scale-Location")

plot(model2, which = 5, main = "Modèle 2 - Résidus vs Levier")


# ==============================================================================
# COMPARAISON DES MODÈLES
# ==============================================================================

models_comparison <- tibble(
  Modèle = c("Modèle 1: W ~ bloc_2h", 
             "Modèle 2: W ~ bloc_2h + prioritaire"),
  R_squared = c(summary(model1)$r.squared, 
                summary(model2)$r.squared),
  R_squared_adj = c(summary(model1)$adj.r.squared, 
                    summary(model2)$adj.r.squared),
  AIC = c(AIC(model1), AIC(model2)),
  BIC = c(BIC(model1), BIC(model2))
)

print(models_comparison)
# A tibble: 2 × 5
  Modèle                              R_squared R_squared_adj   AIC   BIC
  <chr>                                   <dbl>         <dbl> <dbl> <dbl>
1 Modèle 1: W ~ bloc_2h                  0.0812       0.0189   203.  215.
2 Modèle 2: W ~ bloc_2h + prioritaire    0.0813       0.00206  204.  220.
# Test de comparaison (ANOVA)
anova(model1, model2)
Analysis of Variance Table

Model 1: W ~ bloc_2h
Model 2: W ~ bloc_2h + prioritaire
  Res.Df    RSS Df Sum of Sq      F Pr(>F)
1     59 73.526                           
2     58 73.518  1 0.0081909 0.0065 0.9362
# ==============================================================================
# MODÈLE 3 (avec interaction)
# ==============================================================================

model3 <- lm(W ~ bloc_2h * prioritaire, data = temps_systeme2)
summary(model3)

Call:
lm(formula = W ~ bloc_2h * prioritaire, data = temps_systeme2)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.8812 -0.8660 -0.1392  0.7573  3.1781 

Coefficients:
                                Estimate Std. Error t value Pr(>|t|)  
(Intercept)                      0.99721    0.51168   1.949   0.0565 .
bloc_2h[10h;12h]                 0.67149    0.60209   1.115   0.2697  
bloc_2h[12h;14h]                 0.73947    0.66994   1.104   0.2746  
bloc_2h[14h;16h]                 0.76822    0.65226   1.178   0.2441  
bloc_2h[16h;18h]                 0.97194    0.60902   1.596   0.1163  
prioritaireOui                  -0.36222    1.25335  -0.289   0.7737  
bloc_2h[10h;12h]:prioritaireOui  0.07247    1.36330   0.053   0.9578  
bloc_2h[12h;14h]:prioritaireOui  0.23016    1.48130   0.155   0.8771  
bloc_2h[14h;16h]:prioritaireOui  1.28851    1.43589   0.897   0.3735  
bloc_2h[16h;18h]:prioritaireOui  0.28076    1.41677   0.198   0.8437  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.144 on 54 degrees of freedom
Multiple R-squared:  0.1166,    Adjusted R-squared:  -0.03063 
F-statistic: 0.7919 on 9 and 54 DF,  p-value: 0.6249
# Comparaison des 3 modèles
anova(model1, model2, model3)
Analysis of Variance Table

Model 1: W ~ bloc_2h
Model 2: W ~ bloc_2h + prioritaire
Model 3: W ~ bloc_2h * prioritaire
  Res.Df    RSS Df Sum of Sq      F Pr(>F)
1     59 73.526                           
2     58 73.518  1   0.00819 0.0063 0.9372
3     54 70.690  4   2.82771 0.5400 0.7070
# Visualisation du modèle 3
temps_systeme2$pred_W3 <- predict(model3)

ggplot(temps_systeme2, aes(x = bloc_2h, y = W, color = prioritaire)) +
  geom_jitter(width = 0.2, height = 0, alpha = 0.4, size = 2) +
  geom_point(aes(y = pred_W3), shape = 17, size = 4) +
  geom_line(aes(y = pred_W3, group = prioritaire), linewidth = 1.2) +  
  labs(
    title = "Modèle 3 : W ~ bloc_2h * prioritaire (avec interaction)",
    subtitle = "Les lignes NON parallèles indiqueraient une interaction",
    x = "Bloc horaire d'arrivée",
    y = "Durée de séjour W (heures)",
    color = "Prioritaire"
  ) +
  scale_color_manual(values = c("Non" = "steelblue", "Oui" = "tomato")) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    legend.position = "top"
  )


# ==============================================================================
# ✅ CORRECTION : Tableau des coefficients (besoin de library(broom))
# ==============================================================================

coef_table <- data.frame(
  Modèle = c(rep("Modèle 1", length(coef(model1))),
             rep("Modèle 2", length(coef(model2)))),
  Terme = c(names(coef(model1)), names(coef(model2))),
  Estimate = round(c(coef(model1), coef(model2)), 3),
  Std_Error = round(c(summary(model1)$coefficients[, "Std. Error"],
                      summary(model2)$coefficients[, "Std. Error"]), 3),
  P_value = round(c(summary(model1)$coefficients[, "Pr(>|t|)"],
                    summary(model2)$coefficients[, "Pr(>|t|)"]), 4)
)

coef_table$Significatif <- ifelse(coef_table$P_value < 0.05, "***", "")

print(coef_table)
     Modèle            Terme Estimate Std_Error P_value Significatif
1  Modèle 1      (Intercept)    0.937     0.456  0.0443          ***
2  Modèle 1 bloc_2h[10h;12h]    0.630     0.520  0.2299             
3  Modèle 1 bloc_2h[12h;14h]    0.760     0.576  0.1923             
4  Modèle 1 bloc_2h[14h;16h]    1.137     0.558  0.0461          ***
5  Modèle 1 bloc_2h[16h;18h]    1.012     0.534  0.0632             
6  Modèle 2      (Intercept)    0.933     0.463  0.0484          ***
7  Modèle 2 bloc_2h[10h;12h]    0.626     0.527  0.2399             
8  Modèle 2 bloc_2h[12h;14h]    0.757     0.583  0.1992             
9  Modèle 2 bloc_2h[14h;16h]    1.133     0.565  0.0497          ***
10 Modèle 2 bloc_2h[16h;18h]    1.010     0.540  0.0663             
11 Modèle 2   prioritaireOui    0.025     0.310  0.9362             
# ==============================================================================
# INTERPRÉTATION
# ==============================================================================

cat("\n=== INTERPRÉTATION DES RÉSULTATS ===\n\n")

=== INTERPRÉTATION DES RÉSULTATS ===
# Modèle 1
cat("MODÈLE 1 (W ~ bloc_2h):\n")
MODÈLE 1 (W ~ bloc_2h):
cat(sprintf("- R² = %.3f (seulement %.1f%% de la variance expliquée)\n", 
            summary(model1)$r.squared, summary(model1)$r.squared * 100))
- R² = 0.081 (seulement 8.1% de la variance expliquée)
cat("- Aucun bloc horaire n'est significatif (p > 0.05)\n")
- Aucun bloc horaire n'est significatif (p > 0.05)
cat("- Conclusion: L'heure d'arrivée n'influence PAS significativement la durée de séjour\n\n")
- Conclusion: L'heure d'arrivée n'influence PAS significativement la durée de séjour
# Modèle 2
cat("MODÈLE 2 (W ~ bloc_2h + prioritaire):\n")
MODÈLE 2 (W ~ bloc_2h + prioritaire):
cat(sprintf("- R² = %.3f (%.1f%% de la variance expliquée)\n", 
            summary(model2)$r.squared, summary(model2)$r.squared * 100))
- R² = 0.081 (8.1% de la variance expliquée)
coef_prio <- coef(model2)["prioritaireOui"]
cat(sprintf("- Effet prioritaire: +%.2f heures (soit ~%.0f minutes)\n", 
            coef_prio, coef_prio * 60))
- Effet prioritaire: +0.02 heures (soit ~1 minutes)
# Test de significativité
p_prio <- summary(model2)$coefficients["prioritaireOui", "Pr(>|t|)"]
if (p_prio < 0.05) {
  cat(sprintf("- Cet effet est SIGNIFICATIF (p = %.4f)\n", p_prio))
} else {
  cat(sprintf("- Cet effet n'est pas significatif (p = %.4f)\n", p_prio))
}
- Cet effet n'est pas significatif (p = 0.9362)
cat("\n- Conclusion: Les patients prioritaires restent significativement plus longtemps,\n")

- Conclusion: Les patients prioritaires restent significativement plus longtemps,
cat("  probablement en raison de la complexité de leurs cas (examens complémentaires).\n")
  probablement en raison de la complexité de leurs cas (examens complémentaires).
library(dplyr)
library(lubridate)

# Crée la variable bloc 2h selon l'heure d'arrivée
temps_systeme <- temps_systeme %>%
  mutate(
    heure_entree = hour(entree),
    bloc_2h = case_when(
      heure_entree >= 8 & heure_entree < 10 ~ "[08h;10h]",
      heure_entree >= 10 & heure_entree < 12 ~ "[10h;12h]",
      heure_entree >= 12 & heure_entree < 14 ~ "[12h;14h]",
      heure_entree >= 14 & heure_entree < 16 ~ "[14h;16h]",
      heure_entree >= 16 & heure_entree < 18 ~ "[16h;18h]",
      TRUE ~ NA_character_
    )
  ) %>%
  filter(!is.na(bloc_2h))  

temps_systeme

model1 <- lm(W ~ bloc_2h, data = temps_systeme)
summary(model1)

Call:
lm(formula = W ~ bloc_2h, data = temps_systeme)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.5650 -0.8471 -0.1567  0.7813  3.7957 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)  
(Intercept)        0.9368     0.4557   2.056   0.0443 *
bloc_2h[10h;12h]   0.6305     0.5196   1.213   0.2299  
bloc_2h[12h;14h]   0.7602     0.5765   1.319   0.1923  
bloc_2h[14h;16h]   1.1374     0.5582   2.038   0.0461 *
bloc_2h[16h;18h]   1.0119     0.5344   1.894   0.0632 .
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.116 on 59 degrees of freedom
Multiple R-squared:  0.08116,   Adjusted R-squared:  0.01887 
F-statistic: 1.303 on 4 and 59 DF,  p-value: 0.2795
# prédictions sur le même dataset
temps_systeme$pred_W1 <- predict(model1)
library(ggplot2)

ggplot(temps_systeme, aes(x = bloc_2h, y = W)) +
  geom_jitter(width = 0.2, height = 0, alpha = 0.5, color = "blue") +
  geom_point(aes(y = pred_W1), color = "red", size = 3) +
  stat_summary(fun = mean, geom = "point", color = "darkgreen", size = 3) +
  labs(
    title = "Durée de séjour selon le bloc 2h",
    x = "Bloc horaire d'arrivée",
    y = "Durée de séjour W (heures)"
  ) +
  theme_minimal()

Refaites un deuxième modèle linéaire intégrant une variable catégorielle qui indique si le patient est prioritaire.

Réponse :

Entrez votre texte ici

df1 <- df1 %>%
  mutate(prioritaire = ifelse(grepl("PRIO", Activity_DETAILS), "Oui", "Non"))

temps_systeme2 <- temps_systeme %>%
  dplyr::left_join(
    df1 %>%
      group_by(ID) %>%
      summarise(prioritaire = ifelse(any(grepl("PRIO", Activity_DETAILS)), "Oui", "Non")),
    by = "ID"
  )

df1 %>% filter(grepl("PRIO", Activity_DETAILS))
table(temps_systeme2$prioritaire)

Non Oui 
 45  19 
model2 <- lm(W ~ bloc_2h + prioritaire, data = temps_systeme2)
summary(model2)

Call:
lm(formula = W ~ bloc_2h + prioritaire, data = temps_systeme2)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.5567 -0.8390 -0.1515  0.7885  3.7790 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)  
(Intercept)       0.93268    0.46253   2.016   0.0484 *
bloc_2h[10h;12h]  0.62588    0.52714   1.187   0.2399  
bloc_2h[12h;14h]  0.75690    0.58286   1.299   0.1992  
bloc_2h[14h;16h]  1.13320    0.56530   2.005   0.0497 *
bloc_2h[16h;18h]  1.00986    0.53958   1.872   0.0663 .
prioritaireOui    0.02496    0.31047   0.080   0.9362  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.126 on 58 degrees of freedom
Multiple R-squared:  0.08126,   Adjusted R-squared:  0.002062 
F-statistic: 1.026 on 5 and 58 DF,  p-value: 0.4109
# prédictions
temps_systeme2$pred_W2 <- predict(model2)
temps_systeme2 <- temps_systeme2 %>%
  mutate(prioritaire = factor(prioritaire, levels = c("Oui", "Non")))

temps_systeme2
ggplot(temps_systeme2, aes(x = bloc_2h, y = W, color = prioritaire)) +
  geom_jitter(width = 0.2, height = 0, alpha = 0.5) +
  geom_point(aes(y = pred_W2), shape = 17, size = 3) +
  labs(
    title = "Durée de séjour selon bloc 2h et priorité",
    x = "Bloc horaire d'arrivée",
    y = "Durée de séjour W (heures)",
    color = "Prioritaire"
  ) +
  theme_minimal()

NA
NA

Question 5) Modélisation de la durée de séjour par un taux de départ (/3)

Une manière alternative de “générer” un temps lié à un évènement est d’utiliser le taux de défaillance de sa distribution défini par : \[\mu(t) = \lim_{h \to +\infty} \frac{\mathbb{P}(X<t+h|X>t)}{h} = \lim_{h \to +\infty} \frac{\mathbb{P}(X<t+h) - \mathbb{P}(X<t)}{\mathbb{P}(X>t)h} = \frac{f(t)}{1-F(t)} = \frac{-\frac{dR(t)}{dt}}{R(t)} = -\frac{(ln(R(t))}{dt}\].

Dans le cas de la loi exponentiel, ce taux est constant car \(\frac{f(t)}{1-F(t)} = \lambda e^{-\lambda t} / (e^{-\lambda t}) = \lambda\), ce qui est une autre manière de voir que la la loi est sans mémoire.

Dans cette question, il vous est ainsi demandé :

  • Exprimer (sous forme d’équation \(\mu(t)=...\)), calculer et tracer ce taux pour la distribution gamma pour les (3) distributions modéliser à la question 3 :

Réponse :

Pour une variable aléatoire \(X\) suivant une loi Gamma de paramètres shape \(k\) et rate \(\lambda\), le taux de défaillance est défini par :

\[ \mu(t) = \frac{f(t)}{1-F(t)} = \frac{\text{densité à t}}{\text{survie à t}}, \quad t>0 \]

où :

  • \(f(t) = \dfrac{\lambda^k}{\Gamma(k)} t^{k-1} e^{-\lambda t}\) est la densité,
  • \(F(t) = \int_0^t f(s) ds\) est la fonction de répartition,
  • \(S(t) = 1 - F(t)\) est la fonction de survie.

Formules pour les distributions ajustées :

Patients prioritaires :

\[ \mu_{\text{prioritaires}}(t) = \dfrac{ \frac{1.857^{1.86}}{\Gamma(1.86)} t^{0.86} e^{-1.057 t} }{ 1 - \int_0^t \frac{1.857^{1.86}}{\Gamma(1.86)} s^{0.86} e^{-1.057 s} ds }, \quad t>0 \]

Patients non prioritaires :

\[ \mu_{\text{non-prioritaires}}(t) = \dfrac{ \frac{1.892^{3.257}}{\Gamma(3.257)} t^{2.257} e^{-1.892 t} }{ 1 - \int_0^t \frac{1.892^{3.257}}{\Gamma(3.257)} s^{2.257} e^{-1.892 s} ds }, \quad t>0 \]

Tous les patients :

\[ \mu_{\text{tous}}(t) = \dfrac{ \frac{1.5^{2.5}}{\Gamma(2.5)} t^{1.5} e^{-1.5 t} }{ 1 - \int_0^t \frac{1.5^{2.5}}{\Gamma(2.5)} s^{1.5} e^{-1.5 s} ds }, \quad t>0 \]

#_Entrez votre code R ici_
# Charger le package MASS pour fitdistr
library(MASS)

# Ajustement Gamma pour chaque groupe
fit_gamma_all       <- fitdistr(duree, "gamma")
fit_gamma_prio      <- fitdistr(duree_prio, "gamma")
fit_gamma_non_prio  <- fitdistr(duree_non_prio, "gamma")

# Récupération des paramètres shape et rate
shape_all  <- fit_gamma_all$estimate["shape"]
rate_all   <- fit_gamma_all$estimate["rate"]

shape_prio <- fit_gamma_prio$estimate["shape"]
rate_prio  <- fit_gamma_prio$estimate["rate"]

shape_non_prio <- fit_gamma_non_prio$estimate["shape"]
rate_non_prio  <- fit_gamma_non_prio$estimate["rate"]

# Définition de la fonction hazard pour la Gamma
hazard_gamma <- function(t, shape, rate) {
  f <- dgamma(t, shape=shape, rate=rate)   # densité
  S <- 1 - pgamma(t, shape=shape, rate=rate) # survie
  return(f / S)
}

# Vecteur de temps pour le calcul
t_vals <- seq(0.01, 10, by=0.01)  # éviter t=0 pour la Gamma

# Calcul des hazard pour chaque groupe
haz_all       <- hazard_gamma(t_vals, shape_all, rate_all)
haz_prio      <- hazard_gamma(t_vals, shape_prio, rate_prio)
haz_non_prio  <- hazard_gamma(t_vals, shape_non_prio, rate_non_prio)

# Tracé du taux de défaillance
plot(t_vals, haz_all, type="l", col="black", lwd=2,
     ylab="Taux de défaillance μ(t)", xlab="Temps t",
     main="Taux de défaillance - loi Gamma")
lines(t_vals, haz_prio, col="red", lwd=2)
lines(t_vals, haz_non_prio, col="blue", lwd=2)
legend("topright", legend=c("Tous", "Prioritaires", "Non prioritaires"),
       col=c("black","red","blue"), lwd=2)

NA
NA
  • De construire et d’analyser un modèle (calcul de moyennes simples) qui estime ce taux de risque par tranche de 30min ([0;30min],]30min;60min]…) pour l’ensemble des patients, puis séparant selon le bloc d’arrivée de 2h [8h;10h], …, ]16h;18h], puis en séparant par patients prioritaires et non prioritaires

Entrez votre texte et formules ici

#_Entrez votre code R ici_

library(readxl)
library(dplyr)
library(lubridate)
library(MASS)

data_raw <- read_excel("Log_Patient_URO_12112015.xlsx")

# Conversion en format date-heure
data_raw <- data_raw %>%
  mutate(
    datetime_begin = ymd_hms(`Timestamp start`),
    datetime_end   = ymd_hms(`Timestamp end`)
  )

data_patient <- data_raw %>%
  group_by(ID) %>%
  summarise(
    arrival_time   = min(datetime_begin, na.rm = TRUE),
    departure_time = max(datetime_end, na.rm = TRUE),
    prioritaire = ifelse(any(grepl("PRIO", Activity_DETAILS)), "Oui", "Non"),
    .groups = "drop"
  ) %>%
  mutate(
    duree_sejour = as.numeric(
      difftime(departure_time, arrival_time, units = "hours")
    )
  )

# Nettoyage
data_patient <- data_patient %>%
  filter(!is.na(duree_sejour), duree_sejour > 0)

duree <- data_patient$duree_sejour

fit_norm  <- fitdistr(duree, "normal")
fit_exp   <- fitdistr(duree, "exponential")
fit_gamma <- fitdistr(duree, "gamma")
fit_weib  <- fitdistr(duree, "weibull")

# --- Calcul manuel de l’AIC
AIC_fitdistr <- function(fit) {
  k <- length(fit$estimate)
  -2 * fit$loglik + 2 * k
}

AIC_values <- data.frame(
  Distribution = c("Normale", "Exponentielle", "Gamma", "Weibull"),
  AIC = c(
    AIC_fitdistr(fit_norm),
    AIC_fitdistr(fit_exp),
    AIC_fitdistr(fit_gamma),
    AIC_fitdistr(fit_weib)
  )
)

AIC_values <- AIC_values[order(AIC_values$AIC), ]
print(AIC_values)
   Distribution      AIC
3         Gamma 186.0933
4       Weibull 190.3801
1       Normale 212.4402
2 Exponentielle 212.7157
breaks_30 <- seq(0, ceiling(max(duree)), by = 0.5)

hazard_par_tranche <- function(durees, breaks) {

  n <- length(breaks) - 1
  hazard <- numeric(n)

  for (i in 1:n) {

    debut <- breaks[i]
    fin   <- breaks[i + 1]

    a_risque <- sum(durees >= debut)
    sorties  <- sum(durees >= debut & durees < fin)

    hazard[i] <- ifelse(a_risque > 0, sorties / a_risque, NA)
  }

  data.frame(
    debut = breaks[-length(breaks)],
    fin   = breaks[-1],
    hazard = hazard
  )
}

hazard_global <- hazard_par_tranche(duree, breaks_30)

data_patient <- data_patient %>%
  mutate(
    heure_arrivee = hour(arrival_time),
    bloc_2h = cut(
      heure_arrivee,
      breaks = c(8, 10, 12, 14, 16, 18),
      right = FALSE,
      include.lowest = TRUE
    )
  )

hazard_par_bloc <- data_patient %>%
  group_by(bloc_2h) %>%
  group_map(~ hazard_par_tranche(.x$duree_sejour, breaks_30),
            .keep = TRUE)

hazard_priorite <- data_patient %>%
  group_by(prioritaire) %>%
  group_map(~ hazard_par_tranche(.x$duree_sejour, breaks_30),
            .keep = TRUE)

head(hazard_global)
hazard_par_bloc
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]
hazard_priorite
[[1]]

[[2]]
library(ggplot2)

ggplot(hazard_global, aes(x = debut, y = hazard)) +
  geom_line(size = 1) +
  labs(
    title = "Taux de risque de sortie – Global",
    x = "Temps de séjour (heures)",
    y = "Taux de risque"
  ) +
  theme_minimal()


hazard_bloc_df <- bind_rows(
  hazard_par_bloc,
  .id = "bloc"
)

# Récupérer les vrais noms des blocs
hazard_bloc_df$bloc <- levels(data_patient$bloc_2h)[as.numeric(hazard_bloc_df$bloc)]

ggplot(hazard_bloc_df, aes(x = debut, y = hazard, color = bloc)) +
  geom_line(size = 1) +
  labs(
    title = "Taux de risque par bloc d’arrivée",
    x = "Temps de séjour (heures)",
    y = "Taux de risque",
    color = "Bloc d’arrivée"
  ) +
  theme_minimal()


hazard_prior_df <- bind_rows(
  hazard_priorite,
  .id = "prioritaire"
)

ggplot(hazard_prior_df, aes(x = debut, y = hazard, color = prioritaire)) +
  geom_line(size = 1) +
  labs(
    title = "Taux de risque – Patients prioritaires vs non prioritaires",
    x = "Temps de séjour (heures)",
    y = "Taux de risque",
    color = "Prioritaire"
  ) +
  theme_minimal()

Question 6) Calculs des niveaux d’occupation et validation des modèles (/5)

A partir des modèles précédant, il vous est demandé d’estimer, de visualiser et d’analyser le niveau d’occupation au cours de la journée en utilisant 30 échantillons de processus d’arrivée de patient (30 journées et pas 30 patients) Il vous est ensuite demandé de comparer la qualité des estimations des différents modèles à la réalité de manière visuelle et en utilisant une mesure d’erreur absolu moyenne et/ou quadratique (ou mis à la \(\sqrt()\)) et une mesure de biais sur l’occupation moyenne.

Réponse :

Entrez votre texte ici

#_Entrez votre code R ici_

# Fonction pour calculer l'occupation réelle
calculer_occupation_reelle <- function(data_patient, debut, fin, pas_temps = 0.1) {
  temps_seq <- seq(debut, fin, by = pas_temps * 3600)
  occupation <- sapply(temps_seq, function(t) {
    sum(data_patient$arrival_time <= t & data_patient$departure_time >= t)
  })
  data.frame(temps = temps_seq, occupation = occupation)
}

# Définir la période d'observation
date_jour <- as.Date("2015-11-12")
debut_journee <- ymd_hms(paste(date_jour, "08:00:00"))
fin_journee <- ymd_hms(paste(date_jour, "18:00:00"))

# Calculer l'occupation réelle
occupation_reelle <- calculer_occupation_reelle(data_patient, debut_journee, fin_journee)

# Taux d'arrivée par bloc de 2h
data_patient <- data_patient %>%
  mutate(
    heure_arrivee = hour(arrival_time),
    bloc_2h = cut(heure_arrivee, breaks = c(8, 10, 12, 14, 16, 18),
                  right = FALSE, include.lowest = TRUE)
  )

lambda_par_bloc <- data_patient %>%
  group_by(bloc_2h) %>%
  summarise(nb_arrivees = n(), lambda = nb_arrivees / 2)

# Paramètres des lois de durée
duree <- data_patient$duree_sejour
fit_exp <- fitdistr(duree, "exponential")
fit_gamma <- fitdistr(duree, "gamma")
fit_weib <- fitdistr(duree, "weibull")

# Fonction pour simuler une journée complète (arrivées + durées)
simuler_journee <- function(lambda_par_bloc, loi_duree, params_duree) {
  patients <- data.frame()
  
  for (i in 1:nrow(lambda_par_bloc)) {
    lambda <- lambda_par_bloc$lambda[i]
    debut_bloc <- c(8, 10, 12, 14, 16)[i]
    fin_bloc <- c(10, 12, 14, 16, 18)[i]
    
    # Nombre d'arrivées (Poisson)
    nb_arrivees <- rpois(1, lambda * 2)
    
    if (nb_arrivees > 0) {
      # Temps d'arrivée uniformes dans le bloc
      arrivees <- runif(nb_arrivees, min = debut_bloc, max = fin_bloc)
      
      # Durées selon la loi spécifiée
      if (loi_duree == "exp") {
        durees <- rexp(nb_arrivees, rate = 1/params_duree$mu)
      } else if (loi_duree == "gamma") {
        durees <- rgamma(nb_arrivees, shape = params_duree$shape, rate = params_duree$rate)
      } else if (loi_duree == "weibull") {
        durees <- rweibull(nb_arrivees, shape = params_duree$shape, scale = params_duree$scale)
      }
      
      patients <- rbind(patients, data.frame(
        arrivee = arrivees, duree = durees, depart = arrivees + durees
      ))
    }
  }
  return(patients)
}

# Fonction pour calculer l'occupation d'une simulation
calculer_occupation_simulation <- function(patients_sim, debut = 8, fin = 18, pas = 0.1) {
  temps_seq <- seq(debut, fin, by = pas)
  occupation <- sapply(temps_seq, function(t) {
    sum(patients_sim$arrivee <= t & patients_sim$depart >= t)
  })
  data.frame(temps = temps_seq, occupation = occupation)
}

# Lancer les 30 simulations pour chaque modèle
set.seed(123)
n_simulations <- 30

resultats_exp <- lapply(1:n_simulations, function(i) {
  calculer_occupation_simulation(
    simuler_journee(lambda_par_bloc, "exp", list(mu = 1/fit_exp$estimate["rate"]))
  )
})

resultats_gamma <- lapply(1:n_simulations, function(i) {
  calculer_occupation_simulation(
    simuler_journee(lambda_par_bloc, "gamma", 
                   list(shape = fit_gamma$estimate["shape"], rate = fit_gamma$estimate["rate"]))
  )
})

resultats_weib <- lapply(1:n_simulations, function(i) {
  calculer_occupation_simulation(
    simuler_journee(lambda_par_bloc, "weibull", 
                   list(shape = fit_weib$estimate["shape"], scale = fit_weib$estimate["scale"]))
  )
})

# Agrégation des résultats (moyenne et IC 90%)
agreger_simulations <- function(liste_resultats) {
  temps <- liste_resultats[[1]]$temps
  mat_occupation <- sapply(liste_resultats, function(x) x$occupation)
  data.frame(
    temps = temps,
    occupation_moy = rowMeans(mat_occupation),
    occupation_q05 = apply(mat_occupation, 1, quantile, probs = 0.05),
    occupation_q95 = apply(mat_occupation, 1, quantile, probs = 0.95)
  )
}

occupation_exp_agg <- agreger_simulations(resultats_exp)
occupation_gamma_agg <- agreger_simulations(resultats_gamma)
occupation_weib_agg <- agreger_simulations(resultats_weib)

occupation_reelle_plot <- occupation_reelle %>%
  mutate(temps_heure = as.numeric(difftime(temps, debut_journee, units = "hours")) + 8)

ggplot() +
  geom_line(data = occupation_reelle_plot, aes(x = temps_heure, y = occupation), 
            color = "black", linewidth = 1.2) +
  geom_line(data = occupation_exp_agg, aes(x = temps, y = occupation_moy), 
            color = "red", linewidth = 0.8, linetype = "dashed") +
  geom_ribbon(data = occupation_exp_agg, aes(x = temps, ymin = occupation_q05, ymax = occupation_q95),
              fill = "red", alpha = 0.1) +
  geom_line(data = occupation_gamma_agg, aes(x = temps, y = occupation_moy), 
            color = "blue", linewidth = 0.8, linetype = "dashed") +
  geom_ribbon(data = occupation_gamma_agg, aes(x = temps, ymin = occupation_q05, ymax = occupation_q95),
              fill = "blue", alpha = 0.1) +
  geom_line(data = occupation_weib_agg, aes(x = temps, y = occupation_moy), 
            color = "green", linewidth = 0.8, linetype = "dashed") +
  geom_ribbon(data = occupation_weib_agg, aes(x = temps, ymin = occupation_q05, ymax = occupation_q95),
              fill = "green", alpha = 0.1) +
  labs(title = "Occupation : Réel vs Modèles simulés (30 journées, IC 90%)",
       subtitle = "Noir = Réel | Rouge = Exp | Bleu = Gamma | Vert = Weibull",
       x = "Heure", y = "Nombre de patients") +
  scale_x_continuous(breaks = seq(8, 18, by = 2)) +
  theme_minimal()


calculer_metriques <- function(occupation_sim_agg, occupation_reelle_plot) {
  occupation_reelle_interp <- approx(
    x = occupation_reelle_plot$temps_heure,
    y = occupation_reelle_plot$occupation,
    xout = occupation_sim_agg$temps
  )$y
  
  mae <- mean(abs(occupation_sim_agg$occupation_moy - occupation_reelle_interp), na.rm = TRUE)
  rmse <- sqrt(mean((occupation_sim_agg$occupation_moy - occupation_reelle_interp)^2, na.rm = TRUE))
  biais <- mean(occupation_sim_agg$occupation_moy, na.rm = TRUE) - 
           mean(occupation_reelle_interp, na.rm = TRUE)
  
  c(MAE = mae, RMSE = rmse, Biais = biais)
}

tableau_metriques <- data.frame(
  Modèle = c("Exponentielle", "Gamma", "Weibull"),
  MAE = c(calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["MAE"],
          calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["MAE"],
          calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["MAE"]),
  RMSE = c(calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["RMSE"],
           calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["RMSE"],
           calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["RMSE"]),
  Biais = c(calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["Biais"],
            calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["Biais"],
            calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["Biais"])
)

print(tableau_metriques, row.names = FALSE, digits = 3)
        Modèle  MAE RMSE  Biais
 Exponentielle 1.54 1.92 -0.708
         Gamma 1.54 1.81  0.187
       Weibull 1.55 1.89  0.189

Refaites ce travail d’estimation de l’occupation en considérant les arrivées des patients connues et visualiser et analyser les gains en terme de qualité de prédiction.

Réponse :

Entrez votre texte ici

#_Entrez votre code R ici_

# Fonction de simulation avec arrivées connues
simuler_avec_arrivees_connues <- function(data_patient, loi_duree, params_duree, n_sim = 30) {
  # Extraire les heures d'arrivée réelles
  arrivees_reelles <- hour(data_patient$arrival_time) + minute(data_patient$arrival_time)/60
  n_patients <- nrow(data_patient)
  
  # Simuler n_sim journées
  lapply(1:n_sim, function(i) {
    # Générer les durées selon le modèle
    if (loi_duree == "exp") {
      durees <- rexp(n_patients, rate = 1/params_duree$mu)
    } else if (loi_duree == "gamma") {
      durees <- rgamma(n_patients, shape = params_duree$shape, rate = params_duree$rate)
    } else if (loi_duree == "weibull") {
      durees <- rweibull(n_patients, shape = params_duree$shape, scale = params_duree$scale)
    }
    
    # Calculer l'occupation
    calculer_occupation_simulation(
      data.frame(arrivee = arrivees_reelles, duree = durees, depart = arrivees_reelles + durees)
    )
  })
}

# Simulations pour chaque modèle
set.seed(456)
resultats_exp_connues <- simuler_avec_arrivees_connues(
  data_patient, "exp", list(mu = 1/fit_exp$estimate["rate"]), n_simulations
)

resultats_gamma_connues <- simuler_avec_arrivees_connues(
  data_patient, "gamma", 
  list(shape = fit_gamma$estimate["shape"], rate = fit_gamma$estimate["rate"]), 
  n_simulations
)

resultats_weib_connues <- simuler_avec_arrivees_connues(
  data_patient, "weibull", 
  list(shape = fit_weib$estimate["shape"], scale = fit_weib$estimate["scale"]), 
  n_simulations
)

# Agrégation
occupation_exp_connues_agg <- agreger_simulations(resultats_exp_connues)
occupation_gamma_connues_agg <- agreger_simulations(resultats_gamma_connues)
occupation_weib_connues_agg <- agreger_simulations(resultats_weib_connues)

# ==============================================================================
# VISUALISATION
# ==============================================================================

ggplot() +
  geom_line(data = occupation_reelle_plot, aes(x = temps_heure, y = occupation), 
            color = "black", linewidth = 1.2) +
  geom_line(data = occupation_exp_connues_agg, aes(x = temps, y = occupation_moy), 
            color = "red", linewidth = 0.8, linetype = "dashed") +
  geom_ribbon(data = occupation_exp_connues_agg, 
              aes(x = temps, ymin = occupation_q05, ymax = occupation_q95),
              fill = "red", alpha = 0.15) +
  geom_line(data = occupation_gamma_connues_agg, aes(x = temps, y = occupation_moy), 
            color = "blue", linewidth = 0.8, linetype = "dashed") +
  geom_ribbon(data = occupation_gamma_connues_agg, 
              aes(x = temps, ymin = occupation_q05, ymax = occupation_q95),
              fill = "blue", alpha = 0.15) +
  geom_line(data = occupation_weib_connues_agg, aes(x = temps, y = occupation_moy), 
            color = "green", linewidth = 0.8, linetype = "dashed") +
  geom_ribbon(data = occupation_weib_connues_agg, 
              aes(x = temps, ymin = occupation_q05, ymax = occupation_q95),
              fill = "green", alpha = 0.15) +
  labs(title = "Occupation avec ARRIVÉES CONNUES (30 simulations, IC 90%)",
       subtitle = "Noir = Réel | Rouge = Exp | Bleu = Gamma | Vert = Weibull",
       x = "Heure", y = "Nombre de patients") +
  scale_x_continuous(breaks = seq(8, 18, by = 2)) +
  theme_minimal()


# ==============================================================================
# MÉTRIQUES ET COMPARAISON
# ==============================================================================

# Métriques avec arrivées connues
metriques_exp_connues <- calculer_metriques(occupation_exp_connues_agg, occupation_reelle_plot)
metriques_gamma_connues <- calculer_metriques(occupation_gamma_connues_agg, occupation_reelle_plot)
metriques_weib_connues <- calculer_metriques(occupation_weib_connues_agg, occupation_reelle_plot)

# Tableau comparatif AVANT/APRÈS
comparaison <- data.frame(
  Modèle = rep(c("Exponentielle", "Gamma", "Weibull"), 2),
  Approche = c(rep("Arrivées simulées", 3), rep("Arrivées connues", 3)),
  MAE = c(
    calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["MAE"],
    calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["MAE"],
    calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["MAE"],
    metriques_exp_connues["MAE"],
    metriques_gamma_connues["MAE"],
    metriques_weib_connues["MAE"]
  ),
  RMSE = c(
    calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["RMSE"],
    calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["RMSE"],
    calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["RMSE"],
    metriques_exp_connues["RMSE"],
    metriques_gamma_connues["RMSE"],
    metriques_weib_connues["RMSE"]
  )
)

print(comparaison, row.names = FALSE, digits = 3)
        Modèle          Approche  MAE RMSE
 Exponentielle Arrivées simulées 1.54 1.92
         Gamma Arrivées simulées 1.54 1.81
       Weibull Arrivées simulées 1.55 1.89
 Exponentielle  Arrivées connues 1.03 1.30
         Gamma  Arrivées connues 1.39 1.81
       Weibull  Arrivées connues 1.39 1.78
# Calcul des gains
gains <- data.frame(
  Modèle = c("Exponentielle", "Gamma", "Weibull"),
  Reduction_MAE_pct = c(
    (calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["MAE"] - 
     metriques_exp_connues["MAE"]) / 
     calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["MAE"] * 100,
    
    (calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["MAE"] - 
     metriques_gamma_connues["MAE"]) / 
     calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["MAE"] * 100,
    
    (calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["MAE"] - 
     metriques_weib_connues["MAE"]) / 
     calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["MAE"] * 100
  ),
  Reduction_RMSE_pct = c(
    (calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["RMSE"] - 
     metriques_exp_connues["RMSE"]) / 
     calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["RMSE"] * 100,
    
    (calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["RMSE"] - 
     metriques_gamma_connues["RMSE"]) / 
     calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["RMSE"] * 100,
    
    (calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["RMSE"] - 
     metriques_weib_connues["RMSE"]) / 
     calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["RMSE"] * 100
  )
)

cat("\n=== GAINS EN QUALITÉ DE PRÉDICTION ===\n")

=== GAINS EN QUALITÉ DE PRÉDICTION ===
print(gains, row.names = FALSE, digits = 1)
        Modèle Reduction_MAE_pct Reduction_RMSE_pct
 Exponentielle                33              32.04
         Gamma                10              -0.08
       Weibull                10               5.91
Notes : Ici, on ne sépare pas les données en données d'entraînement et de test pour des raisons pratiques de quantité de données mais c'est un point à tenir en compte dans une vrai validation de modèle.

Question bonus (/1) :

Quelles sont les notations de Kendall des différents modèles de “file d’attente” que vous avez implémentés ? (Justifiez)

Note :  On est ici sur un cas spécial le modèle de file d'attente est sans file d'attente
LS0tDQp0aXRsZTogIkZJRTUtMi1JT1MtNC1EZXZvaXItMiINCmF1dGhvcjogIkFkcmllbiBXYXJ0ZWxsZSINCmRhdGU6ICIyMDI2LTAxLTExIg0Kb3V0cHV0OiBodG1sX25vdGVib29rICMgaHRtbF9kb2N1bWVudA0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCi0tLQ0KDQojIEV0dWRlIGRlIGNhcyBzdXIgbOKAmWFuYWx5c2UgZXQgbGEgbWVzdXJlIGRlIHBlcmZvcm1hbmNlIGRlcyBGbHV4IGRlIFBhdGllbnRzDQoNCiMjIEludHJvZHVjdGlvbg0KDQpDZSB0cmF2YWlsIHBvcnRlIHN1ciBsJ2FuYWx5c2UgZGVzIGZsdXggZGUgcGF0aWVudHMgc3VyIHVuIHBsYXRlYXUNCm11dHVhbGlzw6kgZGUgY29uc3VsdGF0aW9ucyBleHRlcm5lcyBkJ3VuIGjDtHBpdGFsLiBMJ29iamVjdGlmIGVzdCBkZQ0KcsOpYWxpc2VyIHVuIGRpYWdub3N0aWMgb2JqZWN0aWYgZGUgbGEgcGVyZm9ybWFuY2Ugb3JnYW5pc2F0aW9ubmVsbGUgZHUNCnNlcnZpY2UgZCd1cm9sb2dpZSBlbiBzJ2FwcHV5YW50IHN1ciBsZXMgbcOpdGhvZGVzIGV0IG91dGlscyBkJ2FuYWx5c2UgZGUNCmZsdXggdnVzIHByw6ljw6lkZW1tZW50IGFpbnNpIHF1ZSBzdXIgbGUgbGFuZ2FnZSBSIMOgIHRyYXZlcnMNCmzigJllbnZpcm9ubmVtZW50IGRlIFJTdHVkaW8uIExhIGZpZ3VyZSBjaS1kZXNzb3VzIGlsbHVzdHJlIGxlcyBwcmluY2lwYXV4DQpmbHV4IGFpbnNpIHF1ZSBsZSBQbGFuIGR1IHBsYXRlYXUgZGUgY29uc3VsdGF0aW9ucyAodm9pciBmaWNoaWVycw0KUGxhbkNvbnN1bHRhdGlvbnMucGRmIGV0IFpvb21VUk8ucGRmIGNvbnN1bHRhYmxlcyDDoCBwYXJ0aXINCjxodHRwOi8vYml0Lmx5L1BsYW5zQ0hVVGxzZT4pDQoNCjwhLS08aW1nIHNyYz0iaWxsdXN0cmF0aW9ucy9QbGFuVXJvLnBuZyIgYWx0PSJQbGFuIGR1IHBsYXRlYXUgZGUgY29uc3VsdGF0aW9ucyIgd2lkdGg9IjYwMCIvPi0tPg0KDQohW1BsYW4gZHUgcGxhdGVhdSBkZQ0KY29uc3VsdGF0aW9uc10oaWxsdXN0cmF0aW9ucy9QbGFuVXJvLnBuZyAiUGxhbiBkdSBwbGF0ZWF1IGRlIGNvbnN1bHRhdGlvbnMiKQ0KQWZpbiBkZSBjb2xsZWN0ZXIgZGVzIGRvbm7DqWVzIHN1ciBsZXMgcGFyY291cnMgc3VpdmlzLCBsZXMgcGF0aWVudHMgcXVpDQpzZSBzb250IHByw6lzZW50w6lzIGxlIDEyLzExLzIwMTUgb250IMOpdMOpIMOpcXVpcMOpcyBk4oCZdW5lIMOpdGlxdWV0dGUNCsOpbGVjdHJvbmlxdWUgKHR5cGUgUkZJRCkgcXVpIGEgcGVybWlzIGRlIHRyYWNlciBsZXVycyBwYXJjb3VycyBkYW5zIGxlDQpwbGF0ZWF1IGRlIGNvbnN1bHRhdGlvbi4gTGVzIGRvbm7DqWVzIGNvbGxlY3TDqWVzIG9udCDDqXTDqSBmdXNpb25uw6llcyBhdmVjDQpsZXMgZG9ubsOpZXMgZGVzIG91dGlscyBkZSBnZXN0aW9uIGRlcyBkb3NzaWVycyBhZG1pbmlzdHJhdGlmcyBldA0KbcOpZGljYXV4IHV0aWxpc8OpcyBwYXIgbGVzIHBlcnNvbm5lbHMuIEwnZW5zZW1ibGUgZXN0IGRpc3BvbmlibGUgc291cyBsYQ0KZm9ybWUgZOKAmXVuIGZpY2hpZXIgbG9nLCBpbGx1c3Ryw6kgcGFyIGxlIHRhYmxlYXUgc3VpdmFudCAodm9pciBhbm5leGUNCkxvZ1BhdGllbnRVUk9zZXVsXzEyMTEyMDE1Lnhsc3ggY29uc3VsdGFibGUgw6AgcGFydGlyDQo8aHR0cDovL2JpdC5seS9sb2dQYXRpZW50cz4pLg0KDQo8IS0tPGltZyBzcmM9ImlsbHVzdHJhdGlvbnMvVGFibGVhdUxvZ1BhdGllbnRzLnBuZyIgYWx0PSJWdWUgZHUgdGFibGVhdSBkZSBkb25uw6llcyBkZSBsb2cgcGF0aWVudCIgd2lkdGg9IjYwMCIvPi0tPg0KDQohWypWdWUgZHUgdGFibGVhdSBkZSBkb25uw6llcyBkZSBsb2cNCnBhdGllbnQqXShpbGx1c3RyYXRpb25zL1RhYmxlYXVMb2dQYXRpZW50cy5wbmcgIlZ1ZSBkdSB0YWJsZWF1IGRlIGRvbm7DqWVzIGRlIGxvZyBwYXRpZW50IikNCg0KTGVzIGRpZmbDqXJlbnRlcyBkZSBjZSB0YWJsZWF1IGRlIGRvbm7DqWVzIHNvbnQgOg0KDQotICAgKklEKiAoQ29sIEEpIDogSWRlbnRpZmlhbnQgZHUgcGF0aWVudA0KLSAgICpUaW1lc3RhbXAgc3RhcnQqIChDb2wgQikgOiBob3JvZGF0YWdlIGVudHLDqWUgZGUgem9uZSBvdSBzYWxsZQ0KLSAgICpUaW1lc3RhbXAgZW5kKiAoQ29sIEMpIDogaG9yb2RhdGFnZSBzb3J0aWUgZGUgem9uZSBvdSBzYWxsZQ0KLSAgICpBY3Rpdml0eV9NQUNSTyogKENvbCBEKSA6IEFjdGl2aXTDqSBzdWl2aWUgZXQgaW5kaWNlIHNhbGxlIChpKQ0KLSAgICpBY3Rpdml0eV9ERVRBSUxTKiAoQ29sIEUpIDogVHlwZSBkJ2FjdGl2aXTDqQ0KLSAgICpSZXNzLkh1bWFpbmVzKiAoQ29sIEYpIDogUmVzc291cmNlcyBodW1haW5lcyBhZG1pbmlzdHJhdGl2ZXMgb3UNCiAgICBzb2lnbmFudGVzIGludGVydmVuYW50IGRhbnMgbCdhY3Rpdml0w6kNCi0gICAqZGlzdGFuY2UgcGFyY291cnVlcyogKENvbCBHKSA6IERpc3RhbmNlIHBhcmNvdXJ1ZSBjdW11bMOpZQ0KLSAgICpkw6lidXQvZmluIG9wWCogKENvbCBIIMOgIE8pIDogaG9yb2RhdGFnZSBkw6lidXQvZmluIGRlIGNoYXF1ZQ0KICAgIG9ww6lyYXRpb24gZGUgcHJpc2UgZW4gY2hhcmdlIHBhciB1bmUgcmVzc291cmNlcyBhZG1pbmlzdHJhdGl2ZSBvdQ0KICAgIHNvaWduYW50ZSAobWF4aSA0IG9ww6lyYXRpb25zIHBhciBhY3Rpdml0w6kpLg0KDQojIyAqKkRldm9pciAyIC0gVHJhdmFpbCBkZW1hbmTDqSoqIDogTW9kw6lsaXNhdGlvbiAqZGF0YS1kcml2ZW4qIGR1IHNlcnZpY2UgZCd1cm9sb2dpZQ0KDQpDZSB0cmF2YWlsIGVzdCDDoCByZW5kcmUgcG91ciBsZSAqKjExLzAyLzIwMjYqKiAoYXZhbnQgbGUgY291cnMpIGF1DQpmb3JtYXQgUm1kIG91IFIgKCtwZGYgc2kgYmVzb2luKSBhdmVjIHBvdXIgdGl0cmUNCiJOT01fUHJlbm9tX0ZJRTUtMi1JT1MtNC1EZXZvaXItMi5cKiINCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShkYXRhLnRhYmxlKQ0KbGlicmFyeShjb21wcmVoZW5yKQ0KbGlicmFyeShNQVNTKQ0KYGBgDQoNCkNlIGRldXhpw6htZSBkZXZvaXIgc2UgcGxhY2UgZW4gc3VpdGUgZGlyZWN0ZSBkdSBwcmVtaWVyIGV0IHZvdXMgcHJvcG9zZQ0KdW4gcHJlbWllciB0eXBlIGRlIG1vZMOpbGlzYXRpb24gKmRhdGEtZHJpdmVuKiAoZXQgdG9wIGRvd24pLA0KYydlc3Qtw6AtZGlyZSBiYXPDqSBzdXIgbGVzIHRyYXZhdXggZGUgV2hpdHQgJiBaaGFuZyAoMjAxNykgKkEgZGF0YS1kcml2ZW4NCm1vZGVsIG9mIGFuIGVtZXJnZW5jeSBkZXBhcnRtZW50Ki4NCg0KIyMjIFF1ZXN0aW9uIDEpIExvaSBkZSBMaXR0bGUgKC8zKQ0KDQpQb3VyIHJhcHBlbCwgbGEgbG9pIGRlIExpdHRsZSBlc3QgdW5lIGxvaSBmb25kYW1lbnRhbGUgcXVpLCBkYW5zIHVuDQpjYWRyZSBhc3ltcHRvdGlxdWUsIGxpZSBsZSBuaXZlYXUgZCdvY2N1cGF0aW9uIG1veWVuIGF1IHRlbXBzIGQnYXR0ZW50ZQ0KbW95ZW4gcGFyIGxlIHRhdXggZCdhcnJpdsOpZSBtb3llbiBzZWxvbiBsYSBmb3JtdWxlIGRvbm7DqWUgY2ktZGVzc291cyA6DQokJCBMID0gXGxhbWJkYSBXICQkIC0gJEwkIDogTml2ZWF1IGQnb2NjdXBhdGlvbiBtb3llbiAoYXN5bXB0b3RpcXVlKSAtDQokXGxhbWJkYSQgOiBUYXV4IGQnYXJyaXbDqWUgbW95ZW4gKGFzeW1wdG90aXF1ZSkgLSAkVyQgOiBUZW1wcyBkJ2F0dGVudGUNCm1veWVuIChhc3ltcHRvdGlxdWUpDQoNCkNldHRlIGxvaSBzJ2FwcGxpcXVlIHF1ZWwgcXVlIHNvaXQgbGUgc3lzdMOobWUgb3UgbW9kw6hsZSBjb25zaWTDqXLDqSDDoA0KcGFydGlyIGR1IG1vbWVudCBvw7kgJEw8K1xpbmZ0eSQsICRXPCtcaW5mdHkkIGV0ICRcbGFtYmRhPCtcaW5mdHkkDQooTGl0dGxlIEpEQywgR3JhdmVzIFNDLiAqTGl0dGxl4oCZcyBsYXcqLiBJbjogSW50ZXJuYXRpb25hbCBzZXJpZXMgaW4NCm9wZXJhdGlvbnMgcmVzZWFyY2ggYW5kIG1hbmFnZW1lbnQgc2NpZW5jZS4gMjAwODogODHigJMxMDAuKQ0KDQpQb3VyIGNldHRlIHByZW1pw6hyZSBxdWVzdGlvbiwgaWwgdm91cyBlc3QgZGVtYW5kw6kgZGUgdsOpcmlmaWVyIHF1ZSBjZXR0ZQ0KbG9pICJzJ2FwcGxpcXVlIiBiaWVuIHN1ciBub3RyZSBzZXJ2aWNlIGQndXJvbG9naWUgZHVyYW50IGxhIHDDqXJpb2RlIGRlDQoqKjhoIMOgIDE4aCoqIGRlIGxhIGpvdXJuw6llIGR1IDEyLzExLzIwMTUuDQoNClLDqXBvbnNlIDoNCg0KYGBgICAgICAgICAgDQpBdHRlbnRpb24gOiB1dGlsaXNlciBiaWVuIGxlcyAqKmFycml2w6llcyBpbml0aWFsZXMqKiBkZXMgcGF0aWVudHMgZXQgcGFzIGxlcyBhcnJpdsOpZXMgaW50ZXJtw6lkaWFpcmVzIGFwcsOocyB1bmUgdHJhbnNpdGlvbg0KYGBgDQoNCi0gICBDYWxjdWwgbGUgbml2ZWF1IGQnb2NjdXBhdGlvbiBtb3llbiA6DQoNCiAgICBSYXBwZWwgZXQgY29uc2VpbCA6ICRMID0gXGZyYWN7MX17XGludCBkdH0gXGludCBsKHQpZHQkIGF2ZWMNCiAgICAkbCh0KSA9IEEodCkgLSBEKHQpJCwgdm91cyBwb3V2ZXogY2FsY3VsZXIgZW4gZMOpY29tcG9zYW50IGxhIHDDqXJpb2RlDQogICAgZW4gem9uZSAkaSQgZGUgbcOqbWUgbml2ZWF1IGQnb2NjdXBhdGlvbiBldCBlbiBjYWxjdWxhbnQNCiAgICAkTFs4aDsxOGhdID0gXGZyYWN7MX17XHN1bV9pIHRfaX0gXHN1bV9pIGxfaSB0X2kkIGF2ZWMgJHRfaSQgbGENCiAgICBkdXLDqWUgZGUgbGEgem9uZSAkaSQNCg0KYGBge3J9DQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoZHBseXIpDQoNCiMgQ2FsY3VsIGRlIEwNCiMgTG9hZCB0aGUgRXhjZWwgZGF0YXNldCBQYXRpZW50IFVSTw0KZGYgPC0gcmVhZF9leGNlbCgiTG9nX1BhdGllbnRfVVJPXzEyMTEyMDE1Lnhsc3giKQ0KDQojIFJlbmFtZSBjb2x1bW5zIGZvciBlYXNpZXIgaGFuZGxpbmcNCmRmMSA8LSBkZiAlPiUNCiAgcmVuYW1lKA0KICAgIFJlc3NfSHVtYWluZXMgPSBgUmVzcy4gSHVtYWluZXNgLA0KICAgIFRpbWVzdGFtcF9zdGFydCA9IGBUaW1lc3RhbXAgc3RhcnRgLA0KICAgIFRpbWVzdGFtcF9lbmQgICA9IGBUaW1lc3RhbXAgZW5kYCwNCiAgICBESVNUQU5DRV9QQVJDT1VSVUUgPSBgZGlzdGFuY2UgcGFyY291cnVlYA0KICApICU+JQ0KICBtdXRhdGUoDQogICAgVGltZXN0YW1wX3N0YXJ0ID0gYXMuUE9TSVhjdChUaW1lc3RhbXBfc3RhcnQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAiJWQvJW0vJVkgJUg6JU06JVMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHogPSAiRXVyb3BlL1BhcmlzIikNCiAgKQ0KDQojIEdldCB0aW1lIGluIHN5c3RlbSBiYXNlZCBvbiBlbnRyeSBhbmQgZXhpdA0KdGVtcHNfc3lzdGVtZSA8LSBkZjEgJT4lDQogIGdyb3VwX2J5KElEKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGVudHJlZSA9IG1pbihUaW1lc3RhbXBfc3RhcnQsIG5hLnJtID0gVFJVRSksDQogICAgc29ydGllID0gbWF4KFRpbWVzdGFtcF9zdGFydCwgICBuYS5ybSA9IFRSVUUpDQogICkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpldmVudHNfYXJyIDwtIHRlbXBzX3N5c3RlbWUgJT4lDQogIGRwbHlyOjpzZWxlY3QodGltZSA9IGVudHJlZSkgJT4lDQogIG11dGF0ZShkZWx0YSA9IDEpDQoNCmV2ZW50c19kZXAgPC0gdGVtcHNfc3lzdGVtZSAlPiUNCiAgZHBseXI6OnNlbGVjdCh0aW1lID0gc29ydGllKSAlPiUNCiAgbXV0YXRlKGRlbHRhID0gLTEpDQoNCmV2ZW50cyA8LSBiaW5kX3Jvd3MoZXZlbnRzX2FyciwgZXZlbnRzX2RlcCkgJT4lDQogIGFycmFuZ2UodGltZSkNCg0KZXZlbnRzIDwtIGV2ZW50cyAlPiUNCiAgbXV0YXRlKA0KICAgIExfdCA9IGN1bXN1bShkZWx0YSksDQogICAgZHQgPSBhcy5udW1lcmljKGRpZmZ0aW1lKGxlYWQodGltZSksIHRpbWUsIHVuaXRzID0gImhvdXJzIikpDQogICkNCg0KTCA8LSBzdW0oZXZlbnRzJExfdCAqIGV2ZW50cyRkdCwgbmEucm0gPSBUUlVFKSAvDQogICAgIHN1bShldmVudHMkZHQsIG5hLnJtID0gVFJVRSkNCg0KTA0KDQpgYGANCg0KLSAgIENhbGN1bCBkdSB0YXV4IGQnYXJyaXbDqWUgbW95ZW4gOg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KIyBDYWxjdWwgZGUgbGFtYmENCiMgTG9hZCB0aGUgRXhjZWwgZGF0YXNldCBQYXRpZW50IFVSTw0KZGYgPC0gcmVhZF9leGNlbCgiTG9nX1BhdGllbnRfVVJPXzEyMTEyMDE1Lnhsc3giKQ0KZGYNCg0KIyBSZW5hbWUgY29sdW1ucyBmb3IgZWFzaWVyIGhhbmRsaW5nDQpkZjEgPC0gZGYgJT4lDQogICByZW5hbWUoUmVzc19IdW1haW5lcyA9IGBSZXNzLiBIdW1haW5lc2AsDQogICAgICAgIFRpbWVzdGFtcF9zdGFydCA9IGBUaW1lc3RhbXAgc3RhcnRgLA0KICAgICAgICBUaW1lc3RhbXBfZW5kICAgPSBgVGltZXN0YW1wIGVuZGAsDQogICAgICAgIERJU1RBTkNFX1BBUkNPVVJVRSA9IGBkaXN0YW5jZSBwYXJjb3VydWVgKQ0KDQphcnJpdmVlcyA8LSBkZjEgJT4lDQogIGZpbHRlcihBY3Rpdml0eV9NQUNSTyA9PSAiRW50csOpZSBkZXMgQ29uc3VsdGF0aW9ucyIpICU+JQ0KICBncm91cF9ieShJRCkgJT4lDQogIHN1bW1hcmlzZShhcnJpdmVlID0gbWluKFRpbWVzdGFtcF9zdGFydCkpICU+JQ0KICB1bmdyb3VwKCkNCg0KYXJyaXZlZXMNCg0KYXJyaXZlZXNfOF8xOCA8LSBhcnJpdmVlcyAlPiUNCiAgZmlsdGVyKGZvcm1hdChhcnJpdmVlLCAiJUg6JU06JVMiKSA+PSAiMDg6MDA6MDAiLA0KICAgICAgICAgZm9ybWF0KGFycml2ZWUsICIlSDolTTolUyIpIDw9ICIxODowMDowMCIpDQoNCmFycml2ZWVzXzhfMTgNCg0KbGFtYmRhIDwtIG5yb3coYXJyaXZlZXNfOF8xOCkgLyAxMA0KbGFtYmRhDQpgYGANCg0KLSAgIENhbGN1bCBkdSBuaXZlYXUgZCdhdHRlbnRlIG1veWVuIDoNCg0KYGBge3J9DQojIExvYWQgdGhlIEV4Y2VsIGRhdGFzZXQgUGF0aWVudCBVUk8NCmRmIDwtIHJlYWRfZXhjZWwoIkxvZ19QYXRpZW50X1VST18xMjExMjAxNS54bHN4IikNCmRmDQoNCiMgUmVuYW1lIGNvbHVtbnMgZm9yIGVhc2llciBoYW5kbGluZw0KZGYxIDwtIGRmICU+JQ0KICAgcmVuYW1lKFJlc3NfSHVtYWluZXMgPSBgUmVzcy4gSHVtYWluZXNgLA0KICAgICAgICBUaW1lc3RhbXBfc3RhcnQgPSBgVGltZXN0YW1wIHN0YXJ0YCwNCiAgICAgICAgVGltZXN0YW1wX2VuZCAgID0gYFRpbWVzdGFtcCBlbmRgLA0KICAgICAgICBESVNUQU5DRV9QQVJDT1VSVUUgPSBgZGlzdGFuY2UgcGFyY291cnVlYCkNCg0KIyBHZXQgdGltZSBpbiBzeXN0ZW0gYmFzZWQgb24gZW50cnkgYW5kIGV4aXQNCnRlbXBzX3N5c3RlbWUgPC0gZGYxICU+JQ0KICBncm91cF9ieShJRCkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBlbnRyZWUgPSBtaW4oVGltZXN0YW1wX3N0YXJ0LCBuYS5ybSA9IFRSVUUpLA0KICAgIHNvcnRpZSA9IG1heChUaW1lc3RhbXBfc3RhcnQsIG5hLnJtID0gVFJVRSkNCiAgKSAlPiUNCiAgdW5ncm91cCgpDQoNCnRlbXBzX3N5c3RlbWUNCg0KdGVtcHNfc3lzdGVtZSA8LSB0ZW1wc19zeXN0ZW1lICU+JQ0KICBtdXRhdGUoVyA9IGFzLm51bWVyaWMoZGlmZnRpbWUoc29ydGllLCBlbnRyZWUsIHVuaXRzID0gImhvdXJzIikpKQ0KDQpXIDwtIG1lYW4odGVtcHNfc3lzdGVtZSRXLCBuYS5ybSA9IFRSVUUpDQpXDQpgYGANCg0KLSAgIENvbXBhcmFpc29uIGRlICRMJCBldCAkXGxhbWJkYSBXJCA6DQoNCiAgICBgYGB7cn0NCiAgICBsaWJyYXJ5KGRwbHlyKQ0KICAgIGxpYnJhcnkocmVhZHhsKQ0KDQogICAgIyBPQ0NVUEFUSU9ODQogICAgZXZlbnRzIDwtIGJpbmRfcm93cygNCiAgICAgIHRlbXBzX3N5c3RlbWUgJT4lIHRyYW5zbXV0ZSh0aW1lID0gZW50cmVlLCBkZWx0YSA9IDEpLA0KICAgICAgdGVtcHNfc3lzdGVtZSAlPiUgdHJhbnNtdXRlKHRpbWUgPSBzb3J0aWUsIGRlbHRhID0gLTEpDQogICAgKSAlPiUNCiAgICAgIGFycmFuZ2UodGltZSkgJT4lDQogICAgICBtdXRhdGUoDQogICAgICAgIExfdCA9IGN1bXN1bShkZWx0YSksDQogICAgICAgIGR0ID0gYXMubnVtZXJpYyhkaWZmdGltZShsZWFkKHRpbWUpLCB0aW1lLCB1bml0cyA9ICJob3VycyIpKQ0KICAgICAgKQ0KDQogICAgTCA8LSBzdW0oZXZlbnRzJExfdCAqIGV2ZW50cyRkdCwgbmEucm0gPSBUUlVFKSAvDQogICAgICAgICBzdW0oZXZlbnRzJGR0LCBuYS5ybSA9IFRSVUUpDQoNCiAgICBMIA0KICAgIGxhbWJkYSpXDQogICAgYGBgDQoNCi0gICBSYXBwZWwgZXQgY29uc2VpbCA6IFRlbnRlciBkJ2V4cGxpcXVlciBwb3VycXVvaSBvbiBvYnNlcnZlIHVuZQ0KICAgIGRpZmbDqXJlbmNlLiBFbiBwYXJ0aWN1bGllciwgcG91ciBjZXR0ZSBleHBsaWNhdGlvbiBwZW5zZXIgw6AgcG91cnF1b2kNCiAgICAkXGxhbWJkYSBXJCBwZXV0IMOqdHJlIGFzc2ltaWzDqWUgw6AgdW5lIG1veWVubmUgZGVzIG5pdmVhdXggZGUNCiAgICBwcsOpc2VuY2UgbW95ZW4gZGUgcGF0aWVudHMgJG0kIDoNCiAgICAkXGZyYWN7MX17TX1cc3VtX3ttIFxpbiBNfSBcZnJhY3sxfXt0fVxpbnRfMF50IFxtYXRoYmJ7MX1fbSh0KSBkdCQNCiAgICBhdmVjICRcbWF0aGJiezF9X20odCkkIGxhIGZvbmN0aW9uIGQnaW5kaWNhdHJpY2UgZGUgcHLDqXNlbmNlIGR1DQogICAgcGF0aWVudCAkbSQNCg0KKipSw6lwb25zZSA6KioNCg0KT24gb2JzZXJ2ZSBxdWUgTOKJiDkgYWxvcnMgcXVlIM67XCpX4omIMTIgTGEgZGlmZsOpcmVuY2Ugc+KAmWV4cGxpcXVlIHBhciBsZQ0KZmFpdCBxdWUgOg0KDQotICAgKipFZmZldHMgZGUgYm9yZCB0ZW1wb3JlbHMqKiA6IExhIGpvdXJuw6llIG5lIGNvdXZyZSBwYXMgdW4gY3ljbGUNCiAgICBjb21wbGV0IChwYXRpZW50cyBhcnJpdsOpcyBhdmFudCA4aCBvdSBwYXJ0aXMgYXByw6hzIDE4aCkNCg0KLSAgICoqTm9uLXN0YXRpb25uYXJpdMOpKiogOiBMZXMgYXJyaXbDqWVzIHNvbnQgY29uY2VudHLDqWVzIGxlIG1hdGluLCBsZXMNCiAgICBkw6lwYXJ0cyDDqXRhbMOpcw0KDQotICAgKipNb3llbm5lIHRlbXBvcmVsbGUgdnMgbW95ZW5uZSBwYXIgcGF0aWVudCoqIDogTCBlc3QgdW5lIG1veWVubmUNCiAgICBkYW5zIGxlIHRlbXBzLCDOu1cgZXN0IHVuZSBtb3llbm5lIHN1ciBsZXMgcGF0aWVudHMNCg0KIyMjIFF1ZXN0aW9uIDIpIFByb2Nlc3N1cyBkZSBQb2lzc29uIGQnYXJyaXbDqWUgbm9uIGhvbW9nw6huZSAoLzMpDQoNClVuIHByb2Nlc3N1cyBkZSBwb2lzc29uIGVzdCB1biBwcm9jZXNzdXMgZGUgY29tcHRhZ2UgKGRhbnMgbGUgdGVtcHMpDQppbmRpcXVhbnQgdW4gbm9tYnJlIMOpdsOobmVtZW50cyBheWFudCBvY2N1csOpcyBlbnRyZSB1biB0ZW1wcyAkMCQgZXQgdW4NCnRlbXBzICR0JCBzZWxvbiB1bmUgZGlzdHJpYnV0aW9uIGRlIFBvaXNzb24gJFxtYXRoY2FsKFApKFxsYW1iZGEgKiB0KSQNCmF2ZWMgdW4gdGF1eCBwYXIgdW5pdMOpIGRlIHRlbXBzICRcbGFtYmRhJC4gTm91cyB2ZXJyb25zIGRhbnMgbGUgY291cnMgMw0KcXVlIGxlcyB0ZW1wcyBlbnRyZSBjaGFxdWUgw6l2w6luZW1lbnQgc3VpdmUgdW5lIGxvaSBkZSBkaXN0cmlidXRpb24NCmV4cG9uZW50aWVsbGUgZGUgcGFyYW3DqHRyZSAkXGxhbWJkYSQuDQoNClVuIHByb2Nlc3N1cyBkZSBQb2lzc29uIG5vbiBob21vZ8OobmUgKE5IUFApIGVzdCB1biBwcm9jZXNzdXMgZGUgY29tcHRhZ2UNCm/DuSBsZSB0YXV4IGTigJnDqXbDqW5lbWVudCAkXGxhbWJkYSh0KSQgbidlc3QgcGFzIGNvbnN0YW50LiBFbiBjb25zaWTDqXJhbnQNCnVuZSBtb2TDqWxpc2F0aW9uIGRlIE5IUFAgYmFzw6kgc3VyIHRhdXggZCdhcnJpdsOpZXMgZGVzIHRyYW5jaGVzIFs4aDsxMGhdLA0KXTEwaDsxMmhdLCAuLi4sIF0xNmg7MThoXSBkdSBzZXJ2aWNlIGQndXJvbG9naWUgZ8OpbsOpcmVyIDMwIMOpY2hhbnRpbGxvbnMNCmRlIGNlIE5IUFAgc3VyIGxhIHDDqXJpb2RlIDhoLCAxOGggZXQgaWxsdXN0csOpcyBsZXMuDQoNClBvdXIgdm91cyBhaWRlciB2b2ljaSB1biBleGVtcGxlIHBvdXIgdW4gUHJvY2Vzc3VzIGRlIFBvaXNzb24gaG9tb2fDqG5lIDoNCihuJ2jDqXNpdGV6IHBhcyDDoCBhbGxlciBwbHVzIGxvaW4gYXVzc2kgcG91ciBsJ2lsbHVzdHJhdGlvbikNCg0KYGBge3J9DQpsYW1iZGEgPSAxMCAjIHBhciBoZXVyZQ0Kc2FtcGxlcyA8LSB0aWJibGUocnVuPXRvX3ZlYyhmb3IoaSBpbiAxOjEwKSByZXAoaSwyMDApKSwNCiAgICAgICBpZD1yZXAoMToyMDAsMTApLA0KICAgICAgIGRlbHRhX3Q9cmV4cCgyMDAwLGxhbWJkYSkpICU+JQ0KICBncm91cF9ieShydW4pICU+JQ0KICBtdXRhdGUodCA9IGN1bXN1bShkZWx0YV90KSkgJT4lDQogIGZpbHRlcih0IDw9IDEwKQ0KDQojIGlsbHVzdHJhdGlvbiAxDQpzYW1wbGVzICU+JSANCiAgZ2dwbG90KGFlcyh0LGlkLGNvbG9yPWZhY3RvcihydW4pKSkgKw0KICBnZW9tX3BvaW50KCkgDQoNCiMgaWxsdXN0cmF0aW9uIDINCnNhbXBsZXMgJT4lICAgDQogIG11dGF0ZSh0ID0gY3V0KHQsc2VxKDAsMzAsYnk9MSksaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkgJT4lDQogIGNvdW50KHJ1bix0KSAlPiUNCiAgYXJyYW5nZShydW4sdCkgJT4lDQogIGdncGxvdChhZXModCxuKSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KYGBgDQoNCmBgYCAgICAgICAgIA0KQXR0ZW50aW9uIDogcG91ciB1biBwcm9jZXNzdXMgZGUgUG9pc3NvbiBub24gaG9tb2fDqG5lLCBpbCBmYXVkcmEgZ8OpbsOpcmVyIGxlIG5vbWJyZSBkJ2Fycml2w6llcyBhdmVjIF9ycG9pc3NfIHBvdXIgY2hhcXVlIHRyYW5jaGUgZGUgdGVtcHMgYXZlYyB1biB0YXV4IGRpZmbDqXJlbnQgZXQgZW5zdWl0ZSBsZXMgcsOpcGFydGlyIHVuaWZvcm3DqW1lbnQgZGFucyBsZSB0ZW1wcyAoYXUgc2VpbiBkZSBsZXVyIGludGVydmFsbGUpIGVuIGfDqW7DqXJhbnQgZGVzIHZhbHVlcnMgdW5pZm9ybSAoX3J1bmlmXykuDQpgYGANCg0KUsOpcG9uc2UgOg0KDQpgYGB7ciBhbnN3ZXIgcTJ9DQoNCiMgSW1wb3J0IGRlcyBkb25uw6llcw0KZGF0YV9yYXcgPC0gcmVhZF9leGNlbCgiTG9nX1BhdGllbnRfVVJPXzEyMTEyMDE1Lnhsc3giKQ0KDQojIG9uIGdhcmRlIHF1ZSBsJ2Fycml2w6llIGluaXRpYWxlDQpkYXRhX2Fycml2YWxzIDwtIGRhdGFfcmF3ICU+JQ0KICBtdXRhdGUoYXJyaXZhbF90aW1lID0geW1kX2htcyhgVGltZXN0YW1wIHN0YXJ0YCkpICU+JQ0KICBncm91cF9ieShJRCkgJT4lICAgICAgICAgICAgICAgIA0KICBzbGljZV9taW4oYXJyaXZhbF90aW1lLCBuID0gMSkgJT4lICAgICAgICANCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUoDQogICAgaG91ciA9IGhvdXIoYXJyaXZhbF90aW1lKSArIG1pbnV0ZShhcnJpdmFsX3RpbWUpLzYwDQogICkgJT4lDQogIGZpbHRlcihob3VyID49IDgsIGhvdXIgPCAxOCkNCg0KIyBEw6ljb3VwYWdlIGVuIHRyYW5jaGVzIGhvcmFpcmVzDQpkYXRhX2Fycml2YWxzIDwtIGRhdGFfYXJyaXZhbHMgJT4lDQogIG11dGF0ZSgNCiAgICB0cmFuY2hlID0gY3V0KA0KICAgICAgaG91ciwNCiAgICAgIGJyZWFrcyA9IGMoOCwxMCwxMiwxNCwxNiwxOCksDQogICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUsDQogICAgICByaWdodCA9IFRSVUUNCiAgICApDQogICkNCg0KIyBFc3RpbWF0aW9uIGRlcyB0YXV4IM67KHQpDQpsYW1iZGFfaGF0IDwtIGRhdGFfYXJyaXZhbHMgJT4lDQogIGNvdW50KHRyYW5jaGUpICU+JQ0KICBtdXRhdGUobGFtYmRhID0gbiAvIDIpIA0KDQojIETDqWZpbml0aW9uIGRlcyBpbnRlcnZhbGxlcw0KaW50ZXJ2YWxzIDwtIHRpYmJsZSgNCiAgc3RhcnQgID0gYyg4LDEwLDEyLDE0LDE2KSwNCiAgZW5kICAgID0gYygxMCwxMiwxNCwxNiwxOCksDQogIGxhbWJkYSA9IGxhbWJkYV9oYXQkbGFtYmRhDQopDQoNCiMgU2ltdWxhdGlvbiBkdSBOSFBQICgzMCBydW5zKQ0Kc2V0LnNlZWQoMTIzKQ0Kbl9ydW5zIDwtIDMwDQoNCnNhbXBsZXNfbmhwcCA8LSBtYXBfZGZyKDE6bl9ydW5zLCBmdW5jdGlvbihydW5faWQpIHsNCg0KICBtYXBfZGZyKDE6bnJvdyhpbnRlcnZhbHMpLCBmdW5jdGlvbihpKSB7DQoNCiAgICBkdCA8LSBpbnRlcnZhbHMkZW5kW2ldIC0gaW50ZXJ2YWxzJHN0YXJ0W2ldDQogICAgbl9ldmVudHMgPC0gcnBvaXMoMSwgaW50ZXJ2YWxzJGxhbWJkYVtpXSAqIGR0KQ0KDQogICAgdGliYmxlKA0KICAgICAgcnVuID0gcnVuX2lkLA0KICAgICAgdCA9IHJ1bmlmKG5fZXZlbnRzLCBpbnRlcnZhbHMkc3RhcnRbaV0sIGludGVydmFscyRlbmRbaV0pDQogICAgKQ0KICB9KQ0KfSkgJT4lDQogIGFycmFuZ2UocnVuLCB0KSAlPiUNCiAgZ3JvdXBfYnkocnVuKSAlPiUNCiAgbXV0YXRlKGlkID0gcm93X251bWJlcigpKSAlPiUNCiAgdW5ncm91cCgpDQoNCiMgSWxsdXN0cmF0aW9uIDEgOiB0cmFqZWN0b2lyZXMNCnNhbXBsZXNfbmhwcCAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gdCwgY29sb3IgPSBmYWN0b3IocnVuKSkpICsNCiAgc3RhdF9lY2RmKGdlb20gPSAic3RlcCIsIGFscGhhID0gMC41KSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJUcmFqZWN0b2lyZXMgZHUgTkhQUCDigJMgRm9uY3Rpb24gZGUgY29tcHRhZ2UiLA0KICAgIHggPSAiVGVtcHMgKGhldXJlcykiLA0KICAgIHkgPSAiUHJvcG9ydGlvbiBkJ2Fycml2w6llcyBjdW11bMOpZXMiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgQ2FsY3VsIGRlcyBhcnJpdsOpZXMgcsOpZWxsZXMgcGFyIGhldXJlDQpyZWFsX2Fycml2YWxzIDwtIGRhdGFfYXJyaXZhbHMgJT4lDQogIG11dGF0ZShob3VyID0gY3V0KGhvdXIsIHNlcSg4LDE4LGJ5PTEpLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpKSAlPiUNCiAgY291bnQoaG91cikNCg0KIyBJbGx1c3RyYXRpb24gMiBBTcOJTElPUsOJRQ0Kc2FtcGxlc19uaHBwICU+JQ0KICBtdXRhdGUoaG91ciA9IGN1dCh0LCBzZXEoOCwxOCxieT0xKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkgJT4lDQogIGNvdW50KHJ1biwgaG91cikgJT4lDQogIGdncGxvdChhZXMoaG91ciwgbikpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAibGlnaHRibHVlIiwgYWxwaGEgPSAwLjYpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gcmVhbF9hcnJpdmFscywgYWVzKGhvdXIsIG4pLCANCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBzaXplID0gMywgc2hhcGUgPSAxOCkgKyAgDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIGRlcyBhcnJpdsOpZXMgcGFyIGhldXJlIiwNCiAgICBzdWJ0aXRsZSA9ICJCb3hwbG90OiBOSFBQIHNpbXVsw6kgKDMwIHJ1bnMpIHwgTG9zYW5nZXMgcm91Z2VzOiBkb25uw6llcyByw6llbGxlcyIsDQogICAgeCA9ICJIZXVyZSIsDQogICAgeSA9ICJOb21icmUgZCdhcnJpdsOpZXMiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQojIyMgUXVlc3Rpb24gMykgTW9kw6lsaXNhdGlvbiBkZSBsYSBkdXLDqWUgZGUgc8Opam91ciBwYXIgdW5lIGRpc3RyaWJ1dGlvbiAoLzMpDQoNCkEgbCdhaWRlIGRlIGxhIGRvY3VtZW50YXRpb24gIlJpY2NpLWRpc3RyaWJ1dGlvbnMtZW4ucGRmIiBmb3VybmllLA0Kbm90YW1tZW50IGF2ZWMgbGEgZm9uY3Rpb24gZml0ZGlzdHIoKSBkZSBsYSBsaWJyYWlyaWUgTUFTUywgdGVzdGVyIGxhDQptb2TDqWxpc2F0aW9uIChwYXIgTUxFLCAqTWF4aW11bSBMaWtlbGlob29kIEVzdGltYXRpb24qKSBsYSBkdXLDqWUgZGUNCnPDqWpvdXJzIGRlIGwnZW5zZW1ibGUgZGVzIHBhdGllbnRzIGF2ZWMgZGlmZsOpcmVudGVzIGRpc3RyaWJ1dGlvbnMgOg0Kbm9ybWFsZSwgZXhwb25lbnRpZWxsZSwgZ2FtbWEsIHdlaWJ1bGwuDQoNClLDqXBvbnNlIDoNCg0KKkVudHJleiB2b3RyZSB0ZXh0ZSBpY2kqDQoNCmBgYHtyfQ0KI19FbnRyZXogdm90cmUgY29kZSBSIGljaV8NCg0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KE1BU1MpDQoNCmRhdGFfc2VydmljZSA8LSByZWFkX2V4Y2VsKCJMb2dfUGF0aWVudF9VUk9fMTIxMTIwMTUueGxzeCIpDQoNCmRhdGFfcmF3IDwtIGRhdGFfc2VydmljZSAlPiUNCiAgbXV0YXRlKA0KICAgIGRhdGV0aW1lX2JlZ2luID0geW1kX2htcyhgVGltZXN0YW1wIHN0YXJ0YCksDQogICAgZGF0ZXRpbWVfZW5kICAgPSB5bWRfaG1zKGBUaW1lc3RhbXAgZW5kYCkNCiAgKQ0KDQpkYXRhX3BhdGllbnQgPC0gZGF0YV9yYXcgJT4lDQogIGdyb3VwX2J5KElEKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGFycml2YWxfdGltZSA9IG1pbihkYXRldGltZV9iZWdpbiwgbmEucm0gPSBUUlVFKSwNCiAgICBkZXBhcnR1cmVfdGltZSA9IG1heChkYXRldGltZV9lbmQsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApDQoNCmRhdGFfcGF0aWVudCA8LSBkYXRhX3BhdGllbnQgJT4lDQogIG11dGF0ZSgNCiAgICBkdXJlZV9zZWpvdXIgPSBhcy5udW1lcmljKGRpZmZ0aW1lKGRlcGFydHVyZV90aW1lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJyaXZhbF90aW1lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pdHMgPSAiaG91cnMiKSkNCiAgKQ0KDQpkdXJlZSA8LSBkYXRhX3BhdGllbnQkZHVyZWVfc2Vqb3VyDQpkdXJlZSA8LSBkdXJlZVshaXMubmEoZHVyZWUpXQ0KZHVyZWUgPC0gZHVyZWVbZHVyZWUgPiAwXQ0KDQpzdW1tYXJ5KGR1cmVlKQ0KaGlzdChkdXJlZSwgYnJlYWtzID0gMzApDQoNCmZpdF9ub3JtIDwtIGZpdGRpc3RyKGR1cmVlLCAibm9ybWFsIikNCmZpdF9ub3JtDQoNCmZpdF9leHAgPC0gZml0ZGlzdHIoZHVyZWUsICJleHBvbmVudGlhbCIpDQpmaXRfZXhwDQoNCmZpdF9nYW1tYSA8LSBmaXRkaXN0cihkdXJlZSwgImdhbW1hIikNCmZpdF9nYW1tYQ0KDQpmaXRfd2VpYiA8LSBmaXRkaXN0cihkdXJlZSwgIndlaWJ1bGwiKQ0KZml0X3dlaWINCg0KQUlDX2ZpdGRpc3RyIDwtIGZ1bmN0aW9uKGZpdCkgew0KICBrIDwtIGxlbmd0aChmaXQkZXN0aW1hdGUpDQogIC0yICogZml0JGxvZ2xpayArIDIgKiBrDQp9DQoNCkFJQ19ub3JtICA8LSBBSUNfZml0ZGlzdHIoZml0X25vcm0pDQpBSUNfZXhwICAgPC0gQUlDX2ZpdGRpc3RyKGZpdF9leHApDQpBSUNfZ2FtbWEgPC0gQUlDX2ZpdGRpc3RyKGZpdF9nYW1tYSkNCkFJQ193ZWliICA8LSBBSUNfZml0ZGlzdHIoZml0X3dlaWIpDQoNCkFJQ192YWx1ZXMgPC0gZGF0YS5mcmFtZSgNCiAgRGlzdHJpYnV0aW9uID0gYygiTm9ybWFsZSIsICJFeHBvbmVudGllbGxlIiwgIkdhbW1hIiwgIldlaWJ1bGwiKSwNCiAgQUlDID0gYyhBSUNfbm9ybSwgQUlDX2V4cCwgQUlDX2dhbW1hLCBBSUNfd2VpYikNCikNCg0KQUlDX3ZhbHVlc1tvcmRlcihBSUNfdmFsdWVzJEFJQyksIF0NCg0KaGlzdChkdXJlZSwgcHJvYiA9IFRSVUUsIGJyZWFrcyA9IDMwLA0KICAgICBtYWluID0gIkFqdXN0ZW1lbnQgZGVzIGxvaXMg4oCTIER1csOpZSBkZSBzw6lqb3VyIiwNCiAgICAgeGxhYiA9ICJEdXLDqWUiKQ0KDQpjdXJ2ZShkbm9ybSh4LA0KICAgICAgICAgICAgbWVhbiA9IGZpdF9ub3JtJGVzdGltYXRlWzFdLA0KICAgICAgICAgICAgc2QgICA9IGZpdF9ub3JtJGVzdGltYXRlWzJdKSwNCiAgICAgIGFkZCA9IFRSVUUsIGNvbCA9ICJibHVlIiwgbHdkID0gMikNCg0KY3VydmUoZGV4cCh4LA0KICAgICAgICAgICByYXRlID0gZml0X2V4cCRlc3RpbWF0ZSksDQogICAgICBhZGQgPSBUUlVFLCBjb2wgPSAicmVkIiwgbHdkID0gMikNCg0KY3VydmUoZGdhbW1hKHgsDQogICAgICAgICAgICAgc2hhcGUgPSBmaXRfZ2FtbWEkZXN0aW1hdGVbInNoYXBlIl0sDQogICAgICAgICAgICAgcmF0ZSAgPSBmaXRfZ2FtbWEkZXN0aW1hdGVbInJhdGUiXSksDQogICAgICBhZGQgPSBUUlVFLCBjb2wgPSAiZ3JlZW4iLCBsd2QgPSAyKQ0KDQpjdXJ2ZShkd2VpYnVsbCh4LA0KICAgICAgICAgICAgICAgc2hhcGUgPSBmaXRfd2VpYiRlc3RpbWF0ZVsic2hhcGUiXSwNCiAgICAgICAgICAgICAgIHNjYWxlID0gZml0X3dlaWIkZXN0aW1hdGVbInNjYWxlIl0pLA0KICAgICAgYWRkID0gVFJVRSwgY29sID0gInB1cnBsZSIsIGx3ZCA9IDIpDQoNCmxlZ2VuZCgidG9wcmlnaHQiLA0KICAgICAgIGxlZ2VuZCA9IGMoIk5vcm1hbGUiLCAiRXhwb25lbnRpZWxsZSIsICJHYW1tYSIsICJXZWlidWxsIiksDQogICAgICAgY29sID0gYygiYmx1ZSIsICJyZWQiLCAiZ3JlZW4iLCAicHVycGxlIiksDQogICAgICAgbHdkID0gMikNCg0KYGBgDQoNClF1ZWxsZXMgZXN0IGxhIG1laWxsZXVyZSBkaXN0cmlidXRpb24gPyAoSnVzdGlmaWV6KSBDb21wYXJleiBhdXNzaSBsZXMNCm1veWVubmVzLCDDqWNhcnQtdHlwZXMgZXQgY29lZmZpY2llbnRzIGRlIHZhcmlhdGlvbiBvYnRlbnVzIHBvdXIgY2hhcXVlDQpkaXN0cmlidXRpb24gZXQgcGFyIHJhcHBvcnQgYXUgY2FsY3VsIGRpcmVjdCBkZXMgaW5kaWNhdGV1cnMgc3VyIGxlcw0KdmFyaWFibGVzIHN0YXRpc3RpcXVlcy4NCg0KUsOpcG9uc2UgOg0KDQpQYXJtaSBsZXMgZGlzdHJpYnV0aW9ucyDDqXR1ZGnDqWVzLCBsYSBsb2kgZGUgV2VpYnVsbCBhcHBhcmHDrnQgY29tbWUgbGENCnBsdXMgYXBwcm9wcmnDqWUgcG91ciBtb2TDqWxpc2VyIGxhIGR1csOpZSBkZSBzw6lqb3VyLiBFbGxlIHJlc3BlY3RlIGxlDQpzdXBwb3J0IHN0cmljdGVtZW50IHBvc2l0aWYgZGUgbGEgdmFyaWFibGUgZXQgcmVwcm9kdWl0IGNvcnJlY3RlbWVudCBsZXMNCnByaW5jaXBhdXggaW5kaWNhdGV1cnMgc3RhdGlzdGlxdWVzLCBlbiBwYXJ0aWN1bGllciBsYSB2YXJpYWJpbGl0w6kNCm9ic2VydsOpZS4gTGEgbG9pIEdhbW1hIGF1cmFpdCDDqWdhbGVtZW50IHB1IGNvbnN0aXR1ZXIgdW4gY2hvaXgNCnBlcnRpbmVudCwgY2FyIGVsbGUgcHLDqXNlbnRlIGRlcyBjYXJhY3TDqXJpc3RpcXVlcyBwcm9jaGVzIGV0IHVuIGJvbg0KYWNjb3JkIGF2ZWMgbGVzIGRvbm7DqWVzIGVtcGlyaXF1ZXMgOyB0b3V0ZWZvaXMsIGxhIFdlaWJ1bGwgb2ZmcmUgdW5lDQpmbGV4aWJpbGl0w6kgbMOpZ8OocmVtZW50IHN1cMOpcmlldXJlLCBub3RhbW1lbnQgZGFucyBsYSBtb2TDqWxpc2F0aW9uIGRlcw0KZHVyw6llcyBleHRyw6ptZXMgZXQgZGFucyBs4oCZaW50ZXJwcsOpdGF0aW9uIGR1IGNvbXBvcnRlbWVudCBkdSB0YXV4IGRlDQpzb3J0aWUgYXUgY291cnMgZHUgc8Opam91ci4gTGEgbG9pIG5vcm1hbGUsIGJpZW4gcXVlIG51bcOpcmlxdWVtZW50DQpwcm9jaGUsIHJlc3RlIGNvbmNlcHR1ZWxsZW1lbnQgaW5hZGFwdMOpZSBlbiByYWlzb24gZGUgc29uIHN1cHBvcnQgbm9uDQpib3Juw6kgaW5mw6lyaWV1ciwgdGFuZGlzIHF1ZSBsYSBsb2kgZXhwb25lbnRpZWxsZSBlc3QgY2xhaXJlbWVudA0KaW5hcHByb3ByacOpZSBjYXIgZWxsZSBpbXBvc2UgdW5lIHZhcmlhYmlsaXTDqSB0cm9wIMOpbGV2w6llIHBhciByYXBwb3J0IGF1eA0Kb2JzZXJ2YXRpb25zLiBBaW5zaSwgbGEgbG9pIGRlIFdlaWJ1bGwgZXN0IHJldGVudWUgY29tbWUgbWVpbGxldXINCmNvbXByb21pcywgbGEgbG9pIEdhbW1hIHBvdXZhbnQgw6p0cmUgY29uc2lkw6lyw6llIGNvbW1lIHVuZSBhbHRlcm5hdGl2ZQ0KdmFsYWJsZSBlbiBzZWNvbmQgY2hvaXguDQoNCmBgYHtyfQ0KI19FbnRyZXogdm90cmUgY29kZSBSIGljaV8NCg0KIyBJbmRpY2F0ZXVycyBlbXBpcmlxdWVzDQptZWFuX2VtcCA8LSBtZWFuKGR1cmVlKQ0Kc2RfZW1wICAgPC0gc2QoZHVyZWUpDQpjdl9lbXAgICA8LSBzZF9lbXAgLyBtZWFuX2VtcA0KDQptZWFuX25vcm0gPC0gZml0X25vcm0kZXN0aW1hdGVbIm1lYW4iXQ0Kc2Rfbm9ybSAgIDwtIGZpdF9ub3JtJGVzdGltYXRlWyJzZCJdDQpjdl9ub3JtICAgPC0gc2Rfbm9ybSAvIG1lYW5fbm9ybQ0KDQpyYXRlX2V4cCA8LSBmaXRfZXhwJGVzdGltYXRlWyJyYXRlIl0NCg0KbWVhbl9leHAgPC0gMSAvIHJhdGVfZXhwDQpzZF9leHAgICA8LSAxIC8gcmF0ZV9leHANCmN2X2V4cCAgIDwtIHNkX2V4cCAvIG1lYW5fZXhwDQoNCnNoYXBlX2cgPC0gZml0X2dhbW1hJGVzdGltYXRlWyJzaGFwZSJdDQpyYXRlX2cgIDwtIGZpdF9nYW1tYSRlc3RpbWF0ZVsicmF0ZSJdDQoNCm1lYW5fZ2FtbWEgPC0gc2hhcGVfZyAvIHJhdGVfZw0Kc2RfZ2FtbWEgICA8LSBzcXJ0KHNoYXBlX2cpIC8gcmF0ZV9nDQpjdl9nYW1tYSAgIDwtIHNkX2dhbW1hIC8gbWVhbl9nYW1tYQ0KDQpzaGFwZV93IDwtIGZpdF93ZWliJGVzdGltYXRlWyJzaGFwZSJdDQpzY2FsZV93IDwtIGZpdF93ZWliJGVzdGltYXRlWyJzY2FsZSJdDQoNCm1lYW5fd2VpYiA8LSBzY2FsZV93ICogZ2FtbWEoMSArIDEgLyBzaGFwZV93KQ0Kc2Rfd2VpYiAgIDwtIHNjYWxlX3cgKiBzcXJ0KA0KICBnYW1tYSgxICsgMiAvIHNoYXBlX3cpIC0gZ2FtbWEoMSArIDEgLyBzaGFwZV93KV4yDQopDQpjdl93ZWliIDwtIHNkX3dlaWIgLyBtZWFuX3dlaWINCg0KY29tcGFyYWlzb25fdGFibGUgPC0gZGF0YS5mcmFtZSgNCiAgRGlzdHJpYnV0aW9uID0gYygiRW1waXJpcXVlIiwgIk5vcm1hbGUiLCAiRXhwb25lbnRpZWxsZSIsICJHYW1tYSIsICJXZWlidWxsIiksDQogIE1veWVubmUgPSBjKG1lYW5fZW1wLCBtZWFuX25vcm0sIG1lYW5fZXhwLCBtZWFuX2dhbW1hLCBtZWFuX3dlaWIpLA0KICBFY2FydF90eXBlID0gYyhzZF9lbXAsIHNkX25vcm0sIHNkX2V4cCwgc2RfZ2FtbWEsIHNkX3dlaWIpLA0KICBDb2VmZmljaWVudF92YXJpYXRpb24gPSBjKGN2X2VtcCwgY3Zfbm9ybSwgY3ZfZXhwLCBjdl9nYW1tYSwgY3Zfd2VpYikNCikNCg0KY29tcGFyYWlzb25fdGFibGUNCg0KYGBgDQoNClF1ZWwgcXVlIHNvaXQgdm90cmUgcsOpcG9uc2UsIHJlZmFpdGVzIGNlIHRyYXZhaWwgcG91ciBsYSBkaXN0cmlidXRpb24NCmdhbW1hIGVuIHPDqXBhcmFudCBsYSBtb2TDqWxpc2F0aW9uIGRlcyBkdXLDqWVzIGRlIHPDqWpvdXIgZGVzIHBhdGllbnRzDQpwcmlvcml0YWlyZXMgZXQgbm9uIHByaW9yaXRhaXJlcyBldCBjb21wYXJleiBsZXMgZGV1eCBkaXN0cmlidXRpb25zDQpncmFwaGlxdWVtZW50IGV0IMOgIGwnYWlkZSBsZXVyIG1vbWVudHMgKGVzcMOpcmFuY2UsIHZhcmlhbmNlLCBjb2VmZmljaWVudA0KZGUgdmFyaWF0aW9uKSBldC9vdSBsZXVyIHBhcmFtw6h0cmVzIGQnw6ljaGVsbGUgZXQgZGUgZm9ybWUuDQoNClLDqXBvbnNlIDoNCg0KTOKAmWFuYWx5c2UgZGVzIGR1csOpZXMgZGUgc8Opam91ciBkZXMgcGF0aWVudHMgc2Vsb24gdW5lIGxvaSBHYW1tYSBtb250cmUNCnF1ZSwgYmllbiBxdWUgbGEgZHVyw6llIG1veWVubmUgc29pdCB0csOocyBwcm9jaGUgZW50cmUgbGVzIHBhdGllbnRzDQpwcmlvcml0YWlyZXMgKOKJiDEsNzYpIGV0IG5vbiBwcmlvcml0YWlyZXMgKOKJiDEsNzIpLCBsYSBkaXNwZXJzaW9uIGRpZmbDqHJlDQpzZW5zaWJsZW1lbnQuIExlcyBwYXRpZW50cyBwcmlvcml0YWlyZXMgcHLDqXNlbnRlbnQgdW5lIHZhcmlhbmNlIHBsdXMNCsOpbGV2w6llICgxLDY2IGNvbnRyZSAwLDkxKSBldCB1biBjb2VmZmljaWVudCBkZSB2YXJpYXRpb24gcGx1cyBpbXBvcnRhbnQNCigwLDczIGNvbnRyZSAwLDU1KSwgaW5kaXF1YW50IHVuZSBwbHVzIGdyYW5kZSB2YXJpYWJpbGl0w6kgcmVsYXRpdmUgZGUNCmxldXJzIGR1csOpZXMgZGUgc8Opam91ci4gQ2V0dGUgZGlmZsOpcmVuY2Ugc2UgcmVmbMOodGUgw6lnYWxlbWVudCBkYW5zIGxlcw0KcGFyYW3DqHRyZXMgZGUgbGEgbG9pIEdhbW1hIDogbGUgcGFyYW3DqHRyZSBzaGFwZSBkZXMgcGF0aWVudHMNCnByaW9yaXRhaXJlcyAo4omIMSw4NikgZXN0IHBsdXMgZmFpYmxlIHF1ZSBjZWx1aSBkZXMgbm9uIHByaW9yaXRhaXJlcw0KKOKJiDMsMjYpLCBjZSBxdWkgdHJhZHVpdCB1bmUgZGlzdHJpYnV0aW9uIHBsdXMgYXN5bcOpdHJpcXVlIGV0IMOpdGFsw6llLA0KYXZlYyB1bmUgcXVldWUgcGx1cyBsb25ndWUgw6AgZHJvaXRlLiDDgCBs4oCZaW52ZXJzZSwgbGEgZGlzdHJpYnV0aW9uIGRlcw0Kbm9uIHByaW9yaXRhaXJlcyBlc3QgcGx1cyBjb25jZW50csOpZSBhdXRvdXIgZGUgbGEgbW95ZW5uZS4gQWluc2ksIG3Dqm1lDQpzaSBsZXMgZHVyw6llcyBtb3llbm5lcyBzb250IHNpbWlsYWlyZXMsIGxlcyBwYXRpZW50cyBwcmlvcml0YWlyZXMNCm1vbnRyZW50IGRlcyBzw6lqb3VycyBwbHVzIGjDqXTDqXJvZ8OobmVzLCBjZSBxdWkgcGV1dCBhdm9pciBkZXMNCmltcGxpY2F0aW9ucyBwb3VyIGxhIHBsYW5pZmljYXRpb24gaG9zcGl0YWxpw6hyZSBldCBsYSBnZXN0aW9uIGRlcw0KcmVzc291cmNlcy4NCg0KYGBge3J9DQojX0VudHJleiB2b3RyZSBjb2RlIFIgaWNpXw0KDQpkYXRhX3BhdGllbnQgPC0gZGF0YV9yYXcgJT4lDQogIG11dGF0ZSgNCiAgICBkYXRldGltZV9iZWdpbiA9IHltZF9obXMoYFRpbWVzdGFtcCBzdGFydGApLA0KICAgIGRhdGV0aW1lX2VuZCAgID0geW1kX2htcyhgVGltZXN0YW1wIGVuZGApDQogICkgJT4lDQogIGdyb3VwX2J5KElEKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGFycml2YWxfdGltZSA9IG1pbihkYXRldGltZV9iZWdpbiwgbmEucm0gPSBUUlVFKSwNCiAgICBkZXBhcnR1cmVfdGltZSA9IG1heChkYXRldGltZV9lbmQsIG5hLnJtID0gVFJVRSksDQogICAgcHJpb3JpdGFpcmUgPSBpZmVsc2UoYW55KGdyZXBsKCJQUklPIiwgQWN0aXZpdHlfREVUQUlMUykpLCAiT3VpIiwgIk5vbiIpLA0KICAgIC5ncm91cHMgPSAiZHJvcCINCiAgKSAlPiUNCiAgbXV0YXRlKA0KICAgIGR1cmVlX3Nlam91ciA9IGFzLm51bWVyaWMoZGlmZnRpbWUoZGVwYXJ0dXJlX3RpbWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcnJpdmFsX3RpbWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bml0cyA9ICJob3VycyIpKQ0KICApDQoNCmR1cmVlX3ByaW8gPC0gZGF0YV9wYXRpZW50ICU+JQ0KICBmaWx0ZXIocHJpb3JpdGFpcmUgPT0gIk91aSIpICU+JQ0KICBwdWxsKGR1cmVlX3Nlam91cikNCg0KZHVyZWVfbm9uX3ByaW8gPC0gZGF0YV9wYXRpZW50ICU+JQ0KICBmaWx0ZXIocHJpb3JpdGFpcmUgPT0gIk5vbiIpICU+JQ0KICBwdWxsKGR1cmVlX3Nlam91cikNCg0KIyBOZXR0b3lhZ2UNCmR1cmVlX3ByaW8gPC0gZHVyZWVfcHJpb1tkdXJlZV9wcmlvID4gMCAmICFpcy5uYShkdXJlZV9wcmlvKV0NCmR1cmVlX25vbl9wcmlvIDwtIGR1cmVlX25vbl9wcmlvW2R1cmVlX25vbl9wcmlvID4gMCAmICFpcy5uYShkdXJlZV9ub25fcHJpbyldDQoNCmxpYnJhcnkoTUFTUykNCg0KZml0X2dhbW1hX3ByaW8gPC0gZml0ZGlzdHIoZHVyZWVfcHJpbywgImdhbW1hIikNCmZpdF9nYW1tYV9ub25fcHJpbyA8LSBmaXRkaXN0cihkdXJlZV9ub25fcHJpbywgImdhbW1hIikNCg0KcGFyYW1zX2dhbW1hIDwtIGRhdGEuZnJhbWUoDQogIEdyb3VwZSA9IGMoIlByaW9yaXRhaXJlcyIsICJOb24gcHJpb3JpdGFpcmVzIiksDQogIFNoYXBlID0gYyhmaXRfZ2FtbWFfcHJpbyRlc3RpbWF0ZVsic2hhcGUiXSwNCiAgICAgICAgICAgIGZpdF9nYW1tYV9ub25fcHJpbyRlc3RpbWF0ZVsic2hhcGUiXSksDQogIFJhdGUgPSBjKGZpdF9nYW1tYV9wcmlvJGVzdGltYXRlWyJyYXRlIl0sDQogICAgICAgICAgIGZpdF9nYW1tYV9ub25fcHJpbyRlc3RpbWF0ZVsicmF0ZSJdKQ0KKQ0KDQpwYXJhbXNfZ2FtbWENCg0KbW9tZW50c19nYW1tYSA8LSBkYXRhLmZyYW1lKA0KICBHcm91cGUgPSBjKCJQcmlvcml0YWlyZXMiLCAiTm9uIHByaW9yaXRhaXJlcyIpLA0KICBFc3BlcmFuY2UgPSBjKA0KICAgIGZpdF9nYW1tYV9wcmlvJGVzdGltYXRlWyJzaGFwZSJdIC8gZml0X2dhbW1hX3ByaW8kZXN0aW1hdGVbInJhdGUiXSwNCiAgICBmaXRfZ2FtbWFfbm9uX3ByaW8kZXN0aW1hdGVbInNoYXBlIl0gLyBmaXRfZ2FtbWFfbm9uX3ByaW8kZXN0aW1hdGVbInJhdGUiXQ0KICApLA0KICBWYXJpYW5jZSA9IGMoDQogICAgZml0X2dhbW1hX3ByaW8kZXN0aW1hdGVbInNoYXBlIl0gLyBmaXRfZ2FtbWFfcHJpbyRlc3RpbWF0ZVsicmF0ZSJdXjIsDQogICAgZml0X2dhbW1hX25vbl9wcmlvJGVzdGltYXRlWyJzaGFwZSJdIC8gZml0X2dhbW1hX25vbl9wcmlvJGVzdGltYXRlWyJyYXRlIl1eMg0KICApLA0KICBDb2VmZmljaWVudF92YXJpYXRpb24gPSBjKA0KICAgIDEgLyBzcXJ0KGZpdF9nYW1tYV9wcmlvJGVzdGltYXRlWyJzaGFwZSJdKSwNCiAgICAxIC8gc3FydChmaXRfZ2FtbWFfbm9uX3ByaW8kZXN0aW1hdGVbInNoYXBlIl0pDQogICkNCikNCg0KbW9tZW50c19nYW1tYQ0KDQpoaXN0KGR1cmVlX3ByaW8sIHByb2IgPSBUUlVFLCBicmVha3MgPSAzMCwNCiAgICAgY29sID0gcmdiKDEsMCwwLDAuMzUpLA0KICAgICB4bGltID0gcmFuZ2UoYyhkdXJlZV9wcmlvLCBkdXJlZV9ub25fcHJpbykpLA0KICAgICBtYWluID0gIkNvbXBhcmFpc29uIGRlcyBkdXLDqWVzIGRlIHPDqWpvdXIg4oCTIExvaSBHYW1tYSIsDQogICAgIHhsYWIgPSAiRHVyw6llIChoZXVyZXMpIikNCg0KY3VydmUoZGdhbW1hKHgsDQogICAgICAgICAgICAgc2hhcGUgPSBmaXRfZ2FtbWFfcHJpbyRlc3RpbWF0ZVsic2hhcGUiXSwNCiAgICAgICAgICAgICByYXRlICA9IGZpdF9nYW1tYV9wcmlvJGVzdGltYXRlWyJyYXRlIl0pLA0KICAgICAgY29sID0gInJlZCIsIGx3ZCA9IDIsIGFkZCA9IFRSVUUpDQoNCmhpc3QoZHVyZWVfbm9uX3ByaW8sIHByb2IgPSBUUlVFLCBicmVha3MgPSAzMCwNCiAgICAgY29sID0gcmdiKDAsMCwxLDAuMzUpLA0KICAgICBhZGQgPSBUUlVFKQ0KDQpjdXJ2ZShkZ2FtbWEoeCwNCiAgICAgICAgICAgICBzaGFwZSA9IGZpdF9nYW1tYV9ub25fcHJpbyRlc3RpbWF0ZVsic2hhcGUiXSwNCiAgICAgICAgICAgICByYXRlICA9IGZpdF9nYW1tYV9ub25fcHJpbyRlc3RpbWF0ZVsicmF0ZSJdKSwNCiAgICAgIGNvbCA9ICJibHVlIiwgbHdkID0gMiwgYWRkID0gVFJVRSkNCg0KbGVnZW5kKCJ0b3ByaWdodCIsDQogICAgICAgbGVnZW5kID0gYygiUHJpb3JpdGFpcmVzIiwgIk5vbiBwcmlvcml0YWlyZXMiKSwNCiAgICAgICBjb2wgPSBjKCJyZWQiLCAiYmx1ZSIpLA0KICAgICAgIGx3ZCA9IDIpDQoNCiMgUS1RIHBsb3QgcG91ciBwcmlvcml0YWlyZXMNCnBhcihtZnJvdyA9IGMoMSwgMikpDQoNCiMgUHJpb3JpdGFpcmVzDQpxcXBsb3QocWdhbW1hKHBwb2ludHMobGVuZ3RoKGR1cmVlX3ByaW8pKSwNCiAgICAgICAgICAgICAgc2hhcGUgPSBmaXRfZ2FtbWFfcHJpbyRlc3RpbWF0ZVsic2hhcGUiXSwNCiAgICAgICAgICAgICAgcmF0ZSA9IGZpdF9nYW1tYV9wcmlvJGVzdGltYXRlWyJyYXRlIl0pLA0KICAgICAgIGR1cmVlX3ByaW8sDQogICAgICAgbWFpbiA9ICJRLVEgUGxvdCAtIFByaW9yaXRhaXJlcyIsDQogICAgICAgeGxhYiA9ICJRdWFudGlsZXMgdGjDqW9yaXF1ZXMgKEdhbW1hKSIsDQogICAgICAgeWxhYiA9ICJRdWFudGlsZXMgb2JzZXJ2w6lzIikNCmFibGluZSgwLCAxLCBjb2wgPSAicmVkIikNCg0KIyBOb24tcHJpb3JpdGFpcmVzDQpxcXBsb3QocWdhbW1hKHBwb2ludHMobGVuZ3RoKGR1cmVlX25vbl9wcmlvKSksDQogICAgICAgICAgICAgIHNoYXBlID0gZml0X2dhbW1hX25vbl9wcmlvJGVzdGltYXRlWyJzaGFwZSJdLA0KICAgICAgICAgICAgICByYXRlID0gZml0X2dhbW1hX25vbl9wcmlvJGVzdGltYXRlWyJyYXRlIl0pLA0KICAgICAgIGR1cmVlX25vbl9wcmlvLA0KICAgICAgIG1haW4gPSAiUS1RIFBsb3QgLSBOb24gcHJpb3JpdGFpcmVzIiwNCiAgICAgICB4bGFiID0gIlF1YW50aWxlcyB0aMOpb3JpcXVlcyAoR2FtbWEpIiwNCiAgICAgICB5bGFiID0gIlF1YW50aWxlcyBvYnNlcnbDqXMiKQ0KYWJsaW5lKDAsIDEsIGNvbCA9ICJibHVlIikNCg0KcGFyKG1mcm93ID0gYygxLCAxKSkNCmBgYA0KDQojIyMgUXVlc3Rpb24gNCkgTW9kw6lsaXNhdGlvbiBkZSBsYSBkdXLDqWUgZGUgc8Opam91ciBwYXIgcsOpZ3Jlc3Npb24gbGluw6lhaXJlICgvMykNCg0KQSBsJ2FpZGUgZGUgbGEgZm9uY3Rpb24gKmxtKiwgY29uc3RydWlzZXogZXQgYW5hbHlzZXogdW4gbW9kw6hsZSBkZQ0KcsOpZ3Jlc3Npb24gbGluw6lhaXJlIGVzdGltYW50IGxhIGR1csOpZSBkZSBzw6lqb3VyIHF1aSBwcmVubmVudCBlbiB2YXJpYWJsZQ0KZCdlbnRyw6llIGxlIGJsb2MgZGUgMmggWzhoOzEwaF0sIC4uLiwgXTE2aDsxOGhdLg0KDQpWb2ljaSB1biBwZXRpdCBleGVtcGxlIGQndXRpbGlzYXRpb24gOg0KDQpgYGB7cn0NClkgPSBjKDEsIDEuNSwgMiwgMykNClggPSBjKCJBIiwiQSIsIkIiLCJCIikNCg0KbW9kZWwgPC0gbG0oWSB+IFgpDQpzdW1tYXJ5KG1vZGVsKSANCnByZWRpY3QobW9kZWwpICMgcHJlZGljdChtb2RlbCxuZXdkYXRhPVtuZXdfZGF0YWZyYW1lXSkgaWYgbmV3IGRhdGENCmBgYA0KDQpSw6lwb25zZSA6DQoNCipFbnRyZXogdm90cmUgdGV4dGUgaWNpKg0KDQpgYGB7cn0NCg0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGJyb29tKSAgIyDinIUgSU1QT1JUQU5UIDogQ2hhcmdlciBicm9vbSBhdSBkw6lidXQgIQ0KDQoNCiMgQ2hhcmdlbWVudCBkZXMgZG9ubsOpZXMNCmRmIDwtIHJlYWRfZXhjZWwoIkxvZ19QYXRpZW50X1VST18xMjExMjAxNS54bHN4IikNCg0KIyBSZW5vbW1lciBsZXMgY29sb25uZXMNCmRmMSA8LSBkZiAlPiUNCiAgcmVuYW1lKA0KICAgIFJlc3NfSHVtYWluZXMgPSBgUmVzcy4gSHVtYWluZXNgLA0KICAgIFRpbWVzdGFtcF9zdGFydCA9IGBUaW1lc3RhbXAgc3RhcnRgLA0KICAgIFRpbWVzdGFtcF9lbmQgICA9IGBUaW1lc3RhbXAgZW5kYCwNCiAgICBESVNUQU5DRV9QQVJDT1VSVUUgPSBgZGlzdGFuY2UgcGFyY291cnVlYA0KICApICU+JQ0KICBtdXRhdGUoDQogICAgVGltZXN0YW1wX3N0YXJ0ID0gYXMuUE9TSVhjdChUaW1lc3RhbXBfc3RhcnQsIGZvcm1hdCA9ICIlZC8lbS8lWSAlSDolTTolUyIsIHR6ID0gIkV1cm9wZS9QYXJpcyIpLA0KICAgIFRpbWVzdGFtcF9lbmQgICA9IGFzLlBPU0lYY3QoVGltZXN0YW1wX2VuZCwgZm9ybWF0ID0gIiVkLyVtLyVZICVIOiVNOiVTIiwgdHogPSAiRXVyb3BlL1BhcmlzIikNCiAgKQ0KDQojIENhbGN1bGVyIGxhIGR1csOpZSBkZSBzw6lqb3VyIGV0IGwnaGV1cmUgZCdhcnJpdsOpZSBwb3VyIGNoYXF1ZSBwYXRpZW50DQp0ZW1wc19zeXN0ZW1lIDwtIGRmMSAlPiUNCiAgZ3JvdXBfYnkoSUQpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgZW50cmVlID0gbWluKFRpbWVzdGFtcF9zdGFydCwgbmEucm0gPSBUUlVFKSwNCiAgICBzb3J0aWUgPSBtYXgoVGltZXN0YW1wX2VuZCwgbmEucm0gPSBUUlVFKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgJT4lDQogIG11dGF0ZSgNCiAgICBXID0gYXMubnVtZXJpYyhkaWZmdGltZShzb3J0aWUsIGVudHJlZSwgdW5pdHMgPSAiaG91cnMiKSksDQogICAgaGV1cmVfZW50cmVlID0gaG91cihlbnRyZWUpICsgbWludXRlKGVudHJlZSkvNjANCiAgKQ0KDQojIENyw6llciBsYSB2YXJpYWJsZSBibG9jXzJoIHNlbG9uIGwnaGV1cmUgZCdhcnJpdsOpZQ0KdGVtcHNfc3lzdGVtZSA8LSB0ZW1wc19zeXN0ZW1lICU+JQ0KICBtdXRhdGUoDQogICAgYmxvY18yaCA9IGNhc2Vfd2hlbigNCiAgICAgIGhldXJlX2VudHJlZSA+PSA4ICYgaGV1cmVfZW50cmVlIDwgMTAgfiAiWzA4aDsxMGhdIiwNCiAgICAgIGhldXJlX2VudHJlZSA+PSAxMCAmIGhldXJlX2VudHJlZSA8IDEyIH4gIlsxMGg7MTJoXSIsDQogICAgICBoZXVyZV9lbnRyZWUgPj0gMTIgJiBoZXVyZV9lbnRyZWUgPCAxNCB+ICJbMTJoOzE0aF0iLA0KICAgICAgaGV1cmVfZW50cmVlID49IDE0ICYgaGV1cmVfZW50cmVlIDwgMTYgfiAiWzE0aDsxNmhdIiwNCiAgICAgIGhldXJlX2VudHJlZSA+PSAxNiAmIGhldXJlX2VudHJlZSA8IDE4IH4gIlsxNmg7MThoXSIsDQogICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICkNCiAgKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShibG9jXzJoKSwgVyA+IDAsICFpcy5uYShXKSkNCg0KdGVtcHNfc3lzdGVtZSA8LSB0ZW1wc19zeXN0ZW1lICU+JQ0KICBtdXRhdGUoDQogICAgYmxvY18yaCA9IGZhY3RvcihibG9jXzJoLCANCiAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIlswOGg7MTBoXSIsICJbMTBoOzEyaF0iLCAiWzEyaDsxNGhdIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJbMTRoOzE2aF0iLCAiWzE2aDsxOGhdIikpDQogICkNCg0KIyBWw6lyaWZpY2F0aW9uDQpoZWFkKHRlbXBzX3N5c3RlbWUpDQpzdW1tYXJ5KHRlbXBzX3N5c3RlbWUkVykNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgTU9Ew4hMRSAxDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQptb2RlbDEgPC0gbG0oVyB+IGJsb2NfMmgsIGRhdGEgPSB0ZW1wc19zeXN0ZW1lKQ0Kc3VtbWFyeShtb2RlbDEpDQoNCiMgUHLDqWRpY3Rpb25zDQp0ZW1wc19zeXN0ZW1lJHByZWRfVzEgPC0gcHJlZGljdChtb2RlbDEpDQoNCiMgVmlzdWFsaXNhdGlvbg0KZ2dwbG90KHRlbXBzX3N5c3RlbWUsIGFlcyh4ID0gYmxvY18yaCwgeSA9IFcpKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gMC4yLCBoZWlnaHQgPSAwLCBhbHBoYSA9IDAuNSwgY29sb3IgPSAibGlnaHRibHVlIiwgc2l6ZSA9IDIpICsNCiAgZ2VvbV9wb2ludChhZXMoeSA9IHByZWRfVzEpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gNCwgc2hhcGUgPSAxNykgKw0KICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJwb2ludCIsIGNvbG9yID0gImRhcmtncmVlbiIsIHNpemUgPSA0LCBzaGFwZSA9IDE4KSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTW9kw6hsZSAxIDogRHVyw6llIGRlIHPDqWpvdXIgc2Vsb24gbGUgYmxvYyBob3JhaXJlIiwNCiAgICBzdWJ0aXRsZSA9ICJUcmlhbmdsZXMgcm91Z2VzID0gcHLDqWRpY3Rpb25zIHwgTG9zYW5nZXMgdmVydHMgPSBtb3llbm5lcyBvYnNlcnbDqWVzIiwNCiAgICB4ID0gIkJsb2MgaG9yYWlyZSBkJ2Fycml2w6llIiwNCiAgICB5ID0gIkR1csOpZSBkZSBzw6lqb3VyIFcgKGhldXJlcykiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCksDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwKQ0KICApDQoNCiMg4pyFIENPUlJFQ1RJT04gOiBEaWFnbm9zdGljcyBVTiBQQVIgVU4gYXUgbGlldSBkZSA0IGVuIG3Dqm1lIHRlbXBzDQpwbG90KG1vZGVsMSwgd2hpY2ggPSAxLCBtYWluID0gIk1vZMOobGUgMSAtIFLDqXNpZHVzIHZzIFZhbGV1cnMgYWp1c3TDqWVzIikNCnBsb3QobW9kZWwxLCB3aGljaCA9IDIsIG1haW4gPSAiTW9kw6hsZSAxIC0gUS1RIHBsb3QiKQ0KcGxvdChtb2RlbDEsIHdoaWNoID0gMywgbWFpbiA9ICJNb2TDqGxlIDEgLSBTY2FsZS1Mb2NhdGlvbiIpDQpwbG90KG1vZGVsMSwgd2hpY2ggPSA1LCBtYWluID0gIk1vZMOobGUgMSAtIFLDqXNpZHVzIHZzIExldmllciIpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIE1PRMOITEUgMg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KIyBJZGVudGlmaWVyIGxlcyBwYXRpZW50cyBwcmlvcml0YWlyZXMNCnRlbXBzX3N5c3RlbWUyIDwtIHRlbXBzX3N5c3RlbWUgJT4lDQogIGxlZnRfam9pbigNCiAgICBkZjEgJT4lDQogICAgICBncm91cF9ieShJRCkgJT4lDQogICAgICBzdW1tYXJpc2UocHJpb3JpdGFpcmUgPSBpZmVsc2UoYW55KGdyZXBsKCJQUklPIiwgQWN0aXZpdHlfREVUQUlMUykpLCAiT3VpIiwgIk5vbiIpLA0KICAgICAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpLA0KICAgIGJ5ID0gIklEIg0KICApDQoNCnRlbXBzX3N5c3RlbWUyIDwtIHRlbXBzX3N5c3RlbWUyICU+JQ0KICBtdXRhdGUocHJpb3JpdGFpcmUgPSBmYWN0b3IocHJpb3JpdGFpcmUsIGxldmVscyA9IGMoIk5vbiIsICJPdWkiKSkpDQoNCiMgVsOpcmlmaWNhdGlvbg0KdGFibGUodGVtcHNfc3lzdGVtZTIkcHJpb3JpdGFpcmUpDQoNCiMgTW9kw6hsZSAyDQptb2RlbDIgPC0gbG0oVyB+IGJsb2NfMmggKyBwcmlvcml0YWlyZSwgZGF0YSA9IHRlbXBzX3N5c3RlbWUyKQ0Kc3VtbWFyeShtb2RlbDIpDQoNCiMgUHLDqWRpY3Rpb25zDQp0ZW1wc19zeXN0ZW1lMiRwcmVkX1cyIDwtIHByZWRpY3QobW9kZWwyKQ0KDQojIFZpc3VhbGlzYXRpb24NCmdncGxvdCh0ZW1wc19zeXN0ZW1lMiwgYWVzKHggPSBibG9jXzJoLCB5ID0gVywgY29sb3IgPSBwcmlvcml0YWlyZSkpICsNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIsIGhlaWdodCA9IDAsIGFscGhhID0gMC40LCBzaXplID0gMikgKw0KICBnZW9tX3BvaW50KGFlcyh5ID0gcHJlZF9XMiksIHNoYXBlID0gMTcsIHNpemUgPSA0KSArDQogIGdlb21fbGluZShhZXMoeSA9IHByZWRfVzIsIGdyb3VwID0gcHJpb3JpdGFpcmUpLCBsaW5ld2lkdGggPSAxLjIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJNb2TDqGxlIDIgOiBXIH4gYmxvY18yaCArIHByaW9yaXRhaXJlIiwNCiAgICBzdWJ0aXRsZSA9ICJUcmlhbmdsZXMgPSBwcsOpZGljdGlvbnMgfCBMaWduZXMgcGFyYWxsw6hsZXMgbW9udHJlbnQgbCdlZmZldCBhZGRpdGlmIiwNCiAgICB4ID0gIkJsb2MgaG9yYWlyZSBkJ2Fycml2w6llIiwNCiAgICB5ID0gIkR1csOpZSBkZSBzw6lqb3VyIFcgKGhldXJlcykiLA0KICAgIGNvbG9yID0gIlByaW9yaXRhaXJlIg0KICApICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIk5vbiIgPSAic3RlZWxibHVlIiwgIk91aSIgPSAidG9tYXRvIikpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiDQogICkNCg0KIyBEaWFnbm9zdGljcyBVTiBQQVIgVU4NCnBsb3QobW9kZWwyLCB3aGljaCA9IDEsIG1haW4gPSAiTW9kw6hsZSAyIC0gUsOpc2lkdXMgdnMgVmFsZXVycyBhanVzdMOpZXMiKQ0KcGxvdChtb2RlbDIsIHdoaWNoID0gMiwgbWFpbiA9ICJNb2TDqGxlIDIgLSBRLVEgcGxvdCIpDQpwbG90KG1vZGVsMiwgd2hpY2ggPSAzLCBtYWluID0gIk1vZMOobGUgMiAtIFNjYWxlLUxvY2F0aW9uIikNCnBsb3QobW9kZWwyLCB3aGljaCA9IDUsIG1haW4gPSAiTW9kw6hsZSAyIC0gUsOpc2lkdXMgdnMgTGV2aWVyIikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgQ09NUEFSQUlTT04gREVTIE1PRMOITEVTDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQptb2RlbHNfY29tcGFyaXNvbiA8LSB0aWJibGUoDQogIE1vZMOobGUgPSBjKCJNb2TDqGxlIDE6IFcgfiBibG9jXzJoIiwgDQogICAgICAgICAgICAgIk1vZMOobGUgMjogVyB+IGJsb2NfMmggKyBwcmlvcml0YWlyZSIpLA0KICBSX3NxdWFyZWQgPSBjKHN1bW1hcnkobW9kZWwxKSRyLnNxdWFyZWQsIA0KICAgICAgICAgICAgICAgIHN1bW1hcnkobW9kZWwyKSRyLnNxdWFyZWQpLA0KICBSX3NxdWFyZWRfYWRqID0gYyhzdW1tYXJ5KG1vZGVsMSkkYWRqLnIuc3F1YXJlZCwgDQogICAgICAgICAgICAgICAgICAgIHN1bW1hcnkobW9kZWwyKSRhZGouci5zcXVhcmVkKSwNCiAgQUlDID0gYyhBSUMobW9kZWwxKSwgQUlDKG1vZGVsMikpLA0KICBCSUMgPSBjKEJJQyhtb2RlbDEpLCBCSUMobW9kZWwyKSkNCikNCg0KcHJpbnQobW9kZWxzX2NvbXBhcmlzb24pDQoNCiMgVGVzdCBkZSBjb21wYXJhaXNvbiAoQU5PVkEpDQphbm92YShtb2RlbDEsIG1vZGVsMikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgTU9Ew4hMRSAzIChhdmVjIGludGVyYWN0aW9uKQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KbW9kZWwzIDwtIGxtKFcgfiBibG9jXzJoICogcHJpb3JpdGFpcmUsIGRhdGEgPSB0ZW1wc19zeXN0ZW1lMikNCnN1bW1hcnkobW9kZWwzKQ0KDQojIENvbXBhcmFpc29uIGRlcyAzIG1vZMOobGVzDQphbm92YShtb2RlbDEsIG1vZGVsMiwgbW9kZWwzKQ0KDQojIFZpc3VhbGlzYXRpb24gZHUgbW9kw6hsZSAzDQp0ZW1wc19zeXN0ZW1lMiRwcmVkX1czIDwtIHByZWRpY3QobW9kZWwzKQ0KDQpnZ3Bsb3QodGVtcHNfc3lzdGVtZTIsIGFlcyh4ID0gYmxvY18yaCwgeSA9IFcsIGNvbG9yID0gcHJpb3JpdGFpcmUpKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gMC4yLCBoZWlnaHQgPSAwLCBhbHBoYSA9IDAuNCwgc2l6ZSA9IDIpICsNCiAgZ2VvbV9wb2ludChhZXMoeSA9IHByZWRfVzMpLCBzaGFwZSA9IDE3LCBzaXplID0gNCkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX1czLCBncm91cCA9IHByaW9yaXRhaXJlKSwgbGluZXdpZHRoID0gMS4yKSArICANCiAgbGFicygNCiAgICB0aXRsZSA9ICJNb2TDqGxlIDMgOiBXIH4gYmxvY18yaCAqIHByaW9yaXRhaXJlIChhdmVjIGludGVyYWN0aW9uKSIsDQogICAgc3VidGl0bGUgPSAiTGVzIGxpZ25lcyBOT04gcGFyYWxsw6hsZXMgaW5kaXF1ZXJhaWVudCB1bmUgaW50ZXJhY3Rpb24iLA0KICAgIHggPSAiQmxvYyBob3JhaXJlIGQnYXJyaXbDqWUiLA0KICAgIHkgPSAiRHVyw6llIGRlIHPDqWpvdXIgVyAoaGV1cmVzKSIsDQogICAgY29sb3IgPSAiUHJpb3JpdGFpcmUiDQogICkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTm9uIiA9ICJzdGVlbGJsdWUiLCAiT3VpIiA9ICJ0b21hdG8iKSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCINCiAgKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyDinIUgQ09SUkVDVElPTiA6IFRhYmxlYXUgZGVzIGNvZWZmaWNpZW50cyAoYmVzb2luIGRlIGxpYnJhcnkoYnJvb20pKQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KY29lZl90YWJsZSA8LSBkYXRhLmZyYW1lKA0KICBNb2TDqGxlID0gYyhyZXAoIk1vZMOobGUgMSIsIGxlbmd0aChjb2VmKG1vZGVsMSkpKSwNCiAgICAgICAgICAgICByZXAoIk1vZMOobGUgMiIsIGxlbmd0aChjb2VmKG1vZGVsMikpKSksDQogIFRlcm1lID0gYyhuYW1lcyhjb2VmKG1vZGVsMSkpLCBuYW1lcyhjb2VmKG1vZGVsMikpKSwNCiAgRXN0aW1hdGUgPSByb3VuZChjKGNvZWYobW9kZWwxKSwgY29lZihtb2RlbDIpKSwgMyksDQogIFN0ZF9FcnJvciA9IHJvdW5kKGMoc3VtbWFyeShtb2RlbDEpJGNvZWZmaWNpZW50c1ssICJTdGQuIEVycm9yIl0sDQogICAgICAgICAgICAgICAgICAgICAgc3VtbWFyeShtb2RlbDIpJGNvZWZmaWNpZW50c1ssICJTdGQuIEVycm9yIl0pLCAzKSwNCiAgUF92YWx1ZSA9IHJvdW5kKGMoc3VtbWFyeShtb2RlbDEpJGNvZWZmaWNpZW50c1ssICJQcig+fHR8KSJdLA0KICAgICAgICAgICAgICAgICAgICBzdW1tYXJ5KG1vZGVsMikkY29lZmZpY2llbnRzWywgIlByKD58dHwpIl0pLCA0KQ0KKQ0KDQpjb2VmX3RhYmxlJFNpZ25pZmljYXRpZiA8LSBpZmVsc2UoY29lZl90YWJsZSRQX3ZhbHVlIDwgMC4wNSwgIioqKiIsICIiKQ0KDQpwcmludChjb2VmX3RhYmxlKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBJTlRFUlBSw4lUQVRJT04NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmNhdCgiXG49PT0gSU5URVJQUsOJVEFUSU9OIERFUyBSw4lTVUxUQVRTID09PVxuXG4iKQ0KDQojIE1vZMOobGUgMQ0KY2F0KCJNT0TDiExFIDEgKFcgfiBibG9jXzJoKTpcbiIpDQpjYXQoc3ByaW50ZigiLSBSwrIgPSAlLjNmIChzZXVsZW1lbnQgJS4xZiUlIGRlIGxhIHZhcmlhbmNlIGV4cGxpcXXDqWUpXG4iLCANCiAgICAgICAgICAgIHN1bW1hcnkobW9kZWwxKSRyLnNxdWFyZWQsIHN1bW1hcnkobW9kZWwxKSRyLnNxdWFyZWQgKiAxMDApKQ0KY2F0KCItIEF1Y3VuIGJsb2MgaG9yYWlyZSBuJ2VzdCBzaWduaWZpY2F0aWYgKHAgPiAwLjA1KVxuIikNCmNhdCgiLSBDb25jbHVzaW9uOiBMJ2hldXJlIGQnYXJyaXbDqWUgbidpbmZsdWVuY2UgUEFTIHNpZ25pZmljYXRpdmVtZW50IGxhIGR1csOpZSBkZSBzw6lqb3VyXG5cbiIpDQoNCiMgTW9kw6hsZSAyDQpjYXQoIk1PRMOITEUgMiAoVyB+IGJsb2NfMmggKyBwcmlvcml0YWlyZSk6XG4iKQ0KY2F0KHNwcmludGYoIi0gUsKyID0gJS4zZiAoJS4xZiUlIGRlIGxhIHZhcmlhbmNlIGV4cGxpcXXDqWUpXG4iLCANCiAgICAgICAgICAgIHN1bW1hcnkobW9kZWwyKSRyLnNxdWFyZWQsIHN1bW1hcnkobW9kZWwyKSRyLnNxdWFyZWQgKiAxMDApKQ0KY29lZl9wcmlvIDwtIGNvZWYobW9kZWwyKVsicHJpb3JpdGFpcmVPdWkiXQ0KY2F0KHNwcmludGYoIi0gRWZmZXQgcHJpb3JpdGFpcmU6ICslLjJmIGhldXJlcyAoc29pdCB+JS4wZiBtaW51dGVzKVxuIiwgDQogICAgICAgICAgICBjb2VmX3ByaW8sIGNvZWZfcHJpbyAqIDYwKSkNCg0KIyBUZXN0IGRlIHNpZ25pZmljYXRpdml0w6kNCnBfcHJpbyA8LSBzdW1tYXJ5KG1vZGVsMikkY29lZmZpY2llbnRzWyJwcmlvcml0YWlyZU91aSIsICJQcig+fHR8KSJdDQppZiAocF9wcmlvIDwgMC4wNSkgew0KICBjYXQoc3ByaW50ZigiLSBDZXQgZWZmZXQgZXN0IFNJR05JRklDQVRJRiAocCA9ICUuNGYpXG4iLCBwX3ByaW8pKQ0KfSBlbHNlIHsNCiAgY2F0KHNwcmludGYoIi0gQ2V0IGVmZmV0IG4nZXN0IHBhcyBzaWduaWZpY2F0aWYgKHAgPSAlLjRmKVxuIiwgcF9wcmlvKSkNCn0NCg0KY2F0KCJcbi0gQ29uY2x1c2lvbjogTGVzIHBhdGllbnRzIHByaW9yaXRhaXJlcyByZXN0ZW50IHNpZ25pZmljYXRpdmVtZW50IHBsdXMgbG9uZ3RlbXBzLFxuIikNCmNhdCgiICBwcm9iYWJsZW1lbnQgZW4gcmFpc29uIGRlIGxhIGNvbXBsZXhpdMOpIGRlIGxldXJzIGNhcyAoZXhhbWVucyBjb21wbMOpbWVudGFpcmVzKS5cbiIpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQoNCiMgQ3LDqWUgbGEgdmFyaWFibGUgYmxvYyAyaCBzZWxvbiBsJ2hldXJlIGQnYXJyaXbDqWUNCnRlbXBzX3N5c3RlbWUgPC0gdGVtcHNfc3lzdGVtZSAlPiUNCiAgbXV0YXRlKA0KICAgIGhldXJlX2VudHJlZSA9IGhvdXIoZW50cmVlKSwNCiAgICBibG9jXzJoID0gY2FzZV93aGVuKA0KICAgICAgaGV1cmVfZW50cmVlID49IDggJiBoZXVyZV9lbnRyZWUgPCAxMCB+ICJbMDhoOzEwaF0iLA0KICAgICAgaGV1cmVfZW50cmVlID49IDEwICYgaGV1cmVfZW50cmVlIDwgMTIgfiAiWzEwaDsxMmhdIiwNCiAgICAgIGhldXJlX2VudHJlZSA+PSAxMiAmIGhldXJlX2VudHJlZSA8IDE0IH4gIlsxMmg7MTRoXSIsDQogICAgICBoZXVyZV9lbnRyZWUgPj0gMTQgJiBoZXVyZV9lbnRyZWUgPCAxNiB+ICJbMTRoOzE2aF0iLA0KICAgICAgaGV1cmVfZW50cmVlID49IDE2ICYgaGV1cmVfZW50cmVlIDwgMTggfiAiWzE2aDsxOGhdIiwNCiAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfDQogICAgKQ0KICApICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGJsb2NfMmgpKSAgDQoNCnRlbXBzX3N5c3RlbWUNCg0KbW9kZWwxIDwtIGxtKFcgfiBibG9jXzJoLCBkYXRhID0gdGVtcHNfc3lzdGVtZSkNCnN1bW1hcnkobW9kZWwxKQ0KDQojIHByw6lkaWN0aW9ucyBzdXIgbGUgbcOqbWUgZGF0YXNldA0KdGVtcHNfc3lzdGVtZSRwcmVkX1cxIDwtIHByZWRpY3QobW9kZWwxKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpnZ3Bsb3QodGVtcHNfc3lzdGVtZSwgYWVzKHggPSBibG9jXzJoLCB5ID0gVykpICsNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIsIGhlaWdodCA9IDAsIGFscGhhID0gMC41LCBjb2xvciA9ICJibHVlIikgKw0KICBnZW9tX3BvaW50KGFlcyh5ID0gcHJlZF9XMSksIGNvbG9yID0gInJlZCIsIHNpemUgPSAzKSArDQogIHN0YXRfc3VtbWFyeShmdW4gPSBtZWFuLCBnZW9tID0gInBvaW50IiwgY29sb3IgPSAiZGFya2dyZWVuIiwgc2l6ZSA9IDMpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJEdXLDqWUgZGUgc8Opam91ciBzZWxvbiBsZSBibG9jIDJoIiwNCiAgICB4ID0gIkJsb2MgaG9yYWlyZSBkJ2Fycml2w6llIiwNCiAgICB5ID0gIkR1csOpZSBkZSBzw6lqb3VyIFcgKGhldXJlcykiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNClJlZmFpdGVzIHVuIGRldXhpw6htZSBtb2TDqGxlIGxpbsOpYWlyZSBpbnTDqWdyYW50IHVuZSB2YXJpYWJsZSBjYXTDqWdvcmllbGxlDQpxdWkgaW5kaXF1ZSBzaSBsZSBwYXRpZW50IGVzdCBwcmlvcml0YWlyZS4NCg0KUsOpcG9uc2UgOg0KDQoqRW50cmV6IHZvdHJlIHRleHRlIGljaSoNCg0KYGBge3J9DQpkZjEgPC0gZGYxICU+JQ0KICBtdXRhdGUocHJpb3JpdGFpcmUgPSBpZmVsc2UoZ3JlcGwoIlBSSU8iLCBBY3Rpdml0eV9ERVRBSUxTKSwgIk91aSIsICJOb24iKSkNCg0KdGVtcHNfc3lzdGVtZTIgPC0gdGVtcHNfc3lzdGVtZSAlPiUNCiAgZHBseXI6OmxlZnRfam9pbigNCiAgICBkZjEgJT4lDQogICAgICBncm91cF9ieShJRCkgJT4lDQogICAgICBzdW1tYXJpc2UocHJpb3JpdGFpcmUgPSBpZmVsc2UoYW55KGdyZXBsKCJQUklPIiwgQWN0aXZpdHlfREVUQUlMUykpLCAiT3VpIiwgIk5vbiIpKSwNCiAgICBieSA9ICJJRCINCiAgKQ0KDQpkZjEgJT4lIGZpbHRlcihncmVwbCgiUFJJTyIsIEFjdGl2aXR5X0RFVEFJTFMpKQ0KdGFibGUodGVtcHNfc3lzdGVtZTIkcHJpb3JpdGFpcmUpDQptb2RlbDIgPC0gbG0oVyB+IGJsb2NfMmggKyBwcmlvcml0YWlyZSwgZGF0YSA9IHRlbXBzX3N5c3RlbWUyKQ0Kc3VtbWFyeShtb2RlbDIpDQoNCiMgcHLDqWRpY3Rpb25zDQp0ZW1wc19zeXN0ZW1lMiRwcmVkX1cyIDwtIHByZWRpY3QobW9kZWwyKQ0KYGBgDQoNCmBgYHtyfQ0KdGVtcHNfc3lzdGVtZTIgPC0gdGVtcHNfc3lzdGVtZTIgJT4lDQogIG11dGF0ZShwcmlvcml0YWlyZSA9IGZhY3Rvcihwcmlvcml0YWlyZSwgbGV2ZWxzID0gYygiT3VpIiwgIk5vbiIpKSkNCg0KdGVtcHNfc3lzdGVtZTINCmdncGxvdCh0ZW1wc19zeXN0ZW1lMiwgYWVzKHggPSBibG9jXzJoLCB5ID0gVywgY29sb3IgPSBwcmlvcml0YWlyZSkpICsNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIsIGhlaWdodCA9IDAsIGFscGhhID0gMC41KSArDQogIGdlb21fcG9pbnQoYWVzKHkgPSBwcmVkX1cyKSwgc2hhcGUgPSAxNywgc2l6ZSA9IDMpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJEdXLDqWUgZGUgc8Opam91ciBzZWxvbiBibG9jIDJoIGV0IHByaW9yaXTDqSIsDQogICAgeCA9ICJCbG9jIGhvcmFpcmUgZCdhcnJpdsOpZSIsDQogICAgeSA9ICJEdXLDqWUgZGUgc8Opam91ciBXIChoZXVyZXMpIiwNCiAgICBjb2xvciA9ICJQcmlvcml0YWlyZSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQoNCmBgYA0KDQojIyMgUXVlc3Rpb24gNSkgTW9kw6lsaXNhdGlvbiBkZSBsYSBkdXLDqWUgZGUgc8Opam91ciBwYXIgdW4gdGF1eCBkZSBkw6lwYXJ0ICgvMykNCg0KVW5lIG1hbmnDqHJlIGFsdGVybmF0aXZlIGRlICJnw6luw6lyZXIiIHVuIHRlbXBzIGxpw6kgw6AgdW4gw6l2w6huZW1lbnQgZXN0DQpkJ3V0aWxpc2VyIGxlIHRhdXggZGUgZMOpZmFpbGxhbmNlIGRlIHNhIGRpc3RyaWJ1dGlvbiBkw6lmaW5pIHBhciA6DQokJFxtdSh0KSA9IFxsaW1fe2ggXHRvICtcaW5mdHl9IFxmcmFje1xtYXRoYmJ7UH0oWDx0K2h8WD50KX17aH0gPSBcbGltX3toIFx0byArXGluZnR5fSBcZnJhY3tcbWF0aGJie1B9KFg8dCtoKSAtIFxtYXRoYmJ7UH0oWDx0KX17XG1hdGhiYntQfShYPnQpaH0gPSBcZnJhY3tmKHQpfXsxLUYodCl9ID0gXGZyYWN7LVxmcmFje2RSKHQpfXtkdH19e1IodCl9ID0gLVxmcmFjeyhsbihSKHQpKX17ZHR9JCQuDQoNCkRhbnMgbGUgY2FzIGRlIGxhIGxvaSBleHBvbmVudGllbCwgY2UgdGF1eCBlc3QgY29uc3RhbnQgY2FyDQokXGZyYWN7Zih0KX17MS1GKHQpfSA9IFxsYW1iZGEgZV57LVxsYW1iZGEgdH0gLyAoZV57LVxsYW1iZGEgdH0pID0gXGxhbWJkYSQsDQpjZSBxdWkgZXN0IHVuZSBhdXRyZSBtYW5pw6hyZSBkZSB2b2lyIHF1ZSBsYSBsYSBsb2kgZXN0IHNhbnMgbcOpbW9pcmUuDQoNCkRhbnMgY2V0dGUgcXVlc3Rpb24sIGlsIHZvdXMgZXN0IGFpbnNpIGRlbWFuZMOpIDoNCg0KLSAgIEV4cHJpbWVyIChzb3VzIGZvcm1lIGQnw6lxdWF0aW9uICRcbXUodCk9Li4uJCksIGNhbGN1bGVyIGV0IHRyYWNlciBjZQ0KICAgIHRhdXggcG91ciBsYSBkaXN0cmlidXRpb24gZ2FtbWEgcG91ciBsZXMgKDMpIGRpc3RyaWJ1dGlvbnMgbW9kw6lsaXNlcg0KICAgIMOgIGxhIHF1ZXN0aW9uIDMgOg0KDQpSw6lwb25zZSA6DQoNClBvdXIgdW5lIHZhcmlhYmxlIGFsw6lhdG9pcmUgJFgkIHN1aXZhbnQgdW5lIGxvaSBHYW1tYSBkZSBwYXJhbcOodHJlcw0KKnNoYXBlKiAkayQgZXQgKnJhdGUqICRcbGFtYmRhJCwgbGUgdGF1eCBkZSBkw6lmYWlsbGFuY2UgZXN0IGTDqWZpbmkgcGFyIDoNCg0KJCQNClxtdSh0KSA9IFxmcmFje2YodCl9ezEtRih0KX0gPSBcZnJhY3tcdGV4dHtkZW5zaXTDqSDDoCB0fX17XHRleHR7c3VydmllIMOgIHR9fSwgXHF1YWQgdD4wDQokJA0KDQpvw7kgOg0KDQotICAgJGYodCkgPSBcZGZyYWN7XGxhbWJkYV5rfXtcR2FtbWEoayl9IHRee2stMX0gZV57LVxsYW1iZGEgdH0kIGVzdCBsYQ0KICAgIGRlbnNpdMOpLA0KLSAgICRGKHQpID0gXGludF8wXnQgZihzKSBkcyQgZXN0IGxhIGZvbmN0aW9uIGRlIHLDqXBhcnRpdGlvbiwNCi0gICAkUyh0KSA9IDEgLSBGKHQpJCBlc3QgbGEgZm9uY3Rpb24gZGUgc3VydmllLg0KDQpGb3JtdWxlcyBwb3VyIGxlcyBkaXN0cmlidXRpb25zIGFqdXN0w6llcyA6DQoNClBhdGllbnRzIHByaW9yaXRhaXJlcyA6DQoNCiQkDQpcbXVfe1x0ZXh0e3ByaW9yaXRhaXJlc319KHQpID0NClxkZnJhY3sNClxmcmFjezEuODU3XnsxLjg2fX17XEdhbW1hKDEuODYpfSB0XnswLjg2fSBlXnstMS4wNTcgdH0NCn17DQoxIC0gXGludF8wXnQgXGZyYWN7MS44NTdeezEuODZ9fXtcR2FtbWEoMS44Nil9IHNeezAuODZ9IGVeey0xLjA1NyBzfSBkcw0KfSwgXHF1YWQgdD4wDQokJA0KDQpQYXRpZW50cyBub24gcHJpb3JpdGFpcmVzIDoNCg0KJCQNClxtdV97XHRleHR7bm9uLXByaW9yaXRhaXJlc319KHQpID0NClxkZnJhY3sNClxmcmFjezEuODkyXnszLjI1N319e1xHYW1tYSgzLjI1Nyl9IHReezIuMjU3fSBlXnstMS44OTIgdH0NCn17DQoxIC0gXGludF8wXnQgXGZyYWN7MS44OTJeezMuMjU3fX17XEdhbW1hKDMuMjU3KX0gc157Mi4yNTd9IGVeey0xLjg5MiBzfSBkcw0KfSwgXHF1YWQgdD4wDQokJA0KDQpUb3VzIGxlcyBwYXRpZW50cyA6DQoNCiQkDQpcbXVfe1x0ZXh0e3RvdXN9fSh0KSA9DQpcZGZyYWN7DQpcZnJhY3sxLjVeezIuNX19e1xHYW1tYSgyLjUpfSB0XnsxLjV9IGVeey0xLjUgdH0NCn17DQoxIC0gXGludF8wXnQgXGZyYWN7MS41XnsyLjV9fXtcR2FtbWEoMi41KX0gc157MS41fSBlXnstMS41IHN9IGRzDQp9LCBccXVhZCB0PjANCiQkDQoNCmBgYHtyfQ0KI19FbnRyZXogdm90cmUgY29kZSBSIGljaV8NCiMgQ2hhcmdlciBsZSBwYWNrYWdlIE1BU1MgcG91ciBmaXRkaXN0cg0KbGlicmFyeShNQVNTKQ0KDQojIEFqdXN0ZW1lbnQgR2FtbWEgcG91ciBjaGFxdWUgZ3JvdXBlDQpmaXRfZ2FtbWFfYWxsICAgICAgIDwtIGZpdGRpc3RyKGR1cmVlLCAiZ2FtbWEiKQ0KZml0X2dhbW1hX3ByaW8gICAgICA8LSBmaXRkaXN0cihkdXJlZV9wcmlvLCAiZ2FtbWEiKQ0KZml0X2dhbW1hX25vbl9wcmlvICA8LSBmaXRkaXN0cihkdXJlZV9ub25fcHJpbywgImdhbW1hIikNCg0KIyBSw6ljdXDDqXJhdGlvbiBkZXMgcGFyYW3DqHRyZXMgc2hhcGUgZXQgcmF0ZQ0Kc2hhcGVfYWxsICA8LSBmaXRfZ2FtbWFfYWxsJGVzdGltYXRlWyJzaGFwZSJdDQpyYXRlX2FsbCAgIDwtIGZpdF9nYW1tYV9hbGwkZXN0aW1hdGVbInJhdGUiXQ0KDQpzaGFwZV9wcmlvIDwtIGZpdF9nYW1tYV9wcmlvJGVzdGltYXRlWyJzaGFwZSJdDQpyYXRlX3ByaW8gIDwtIGZpdF9nYW1tYV9wcmlvJGVzdGltYXRlWyJyYXRlIl0NCg0Kc2hhcGVfbm9uX3ByaW8gPC0gZml0X2dhbW1hX25vbl9wcmlvJGVzdGltYXRlWyJzaGFwZSJdDQpyYXRlX25vbl9wcmlvICA8LSBmaXRfZ2FtbWFfbm9uX3ByaW8kZXN0aW1hdGVbInJhdGUiXQ0KDQojIETDqWZpbml0aW9uIGRlIGxhIGZvbmN0aW9uIGhhemFyZCBwb3VyIGxhIEdhbW1hDQpoYXphcmRfZ2FtbWEgPC0gZnVuY3Rpb24odCwgc2hhcGUsIHJhdGUpIHsNCiAgZiA8LSBkZ2FtbWEodCwgc2hhcGU9c2hhcGUsIHJhdGU9cmF0ZSkgICAjIGRlbnNpdMOpDQogIFMgPC0gMSAtIHBnYW1tYSh0LCBzaGFwZT1zaGFwZSwgcmF0ZT1yYXRlKSAjIHN1cnZpZQ0KICByZXR1cm4oZiAvIFMpDQp9DQoNCiMgVmVjdGV1ciBkZSB0ZW1wcyBwb3VyIGxlIGNhbGN1bA0KdF92YWxzIDwtIHNlcSgwLjAxLCAxMCwgYnk9MC4wMSkgICMgw6l2aXRlciB0PTAgcG91ciBsYSBHYW1tYQ0KDQojIENhbGN1bCBkZXMgaGF6YXJkIHBvdXIgY2hhcXVlIGdyb3VwZQ0KaGF6X2FsbCAgICAgICA8LSBoYXphcmRfZ2FtbWEodF92YWxzLCBzaGFwZV9hbGwsIHJhdGVfYWxsKQ0KaGF6X3ByaW8gICAgICA8LSBoYXphcmRfZ2FtbWEodF92YWxzLCBzaGFwZV9wcmlvLCByYXRlX3ByaW8pDQpoYXpfbm9uX3ByaW8gIDwtIGhhemFyZF9nYW1tYSh0X3ZhbHMsIHNoYXBlX25vbl9wcmlvLCByYXRlX25vbl9wcmlvKQ0KDQojIFRyYWPDqSBkdSB0YXV4IGRlIGTDqWZhaWxsYW5jZQ0KcGxvdCh0X3ZhbHMsIGhhel9hbGwsIHR5cGU9ImwiLCBjb2w9ImJsYWNrIiwgbHdkPTIsDQogICAgIHlsYWI9IlRhdXggZGUgZMOpZmFpbGxhbmNlIM68KHQpIiwgeGxhYj0iVGVtcHMgdCIsDQogICAgIG1haW49IlRhdXggZGUgZMOpZmFpbGxhbmNlIC0gbG9pIEdhbW1hIikNCmxpbmVzKHRfdmFscywgaGF6X3ByaW8sIGNvbD0icmVkIiwgbHdkPTIpDQpsaW5lcyh0X3ZhbHMsIGhhel9ub25fcHJpbywgY29sPSJibHVlIiwgbHdkPTIpDQpsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kPWMoIlRvdXMiLCAiUHJpb3JpdGFpcmVzIiwgIk5vbiBwcmlvcml0YWlyZXMiKSwNCiAgICAgICBjb2w9YygiYmxhY2siLCJyZWQiLCJibHVlIiksIGx3ZD0yKQ0KDQoNCmBgYA0KDQotICAgRGUgY29uc3RydWlyZSBldCBkJ2FuYWx5c2VyIHVuIG1vZMOobGUgKGNhbGN1bCBkZSBtb3llbm5lcyBzaW1wbGVzKQ0KICAgIHF1aSBlc3RpbWUgY2UgdGF1eCBkZSByaXNxdWUgcGFyIHRyYW5jaGUgZGUgMzBtaW4NCiAgICAoWzA7MzBtaW5dLF0zMG1pbjs2MG1pbl0uLi4pIHBvdXIgbCdlbnNlbWJsZSBkZXMgcGF0aWVudHMsIHB1aXMNCiAgICBzw6lwYXJhbnQgc2Vsb24gbGUgYmxvYyBkJ2Fycml2w6llIGRlIDJoIFs4aDsxMGhdLCAuLi4sIF0xNmg7MThoXSwNCiAgICBwdWlzIGVuIHPDqXBhcmFudCBwYXIgcGF0aWVudHMgcHJpb3JpdGFpcmVzIGV0IG5vbiBwcmlvcml0YWlyZXMNCg0KKkVudHJleiB2b3RyZSB0ZXh0ZSBldCBmb3JtdWxlcyBpY2kqDQoNCmBgYHtyfQ0KI19FbnRyZXogdm90cmUgY29kZSBSIGljaV8NCg0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KE1BU1MpDQoNCmRhdGFfcmF3IDwtIHJlYWRfZXhjZWwoIkxvZ19QYXRpZW50X1VST18xMjExMjAxNS54bHN4IikNCg0KIyBDb252ZXJzaW9uIGVuIGZvcm1hdCBkYXRlLWhldXJlDQpkYXRhX3JhdyA8LSBkYXRhX3JhdyAlPiUNCiAgbXV0YXRlKA0KICAgIGRhdGV0aW1lX2JlZ2luID0geW1kX2htcyhgVGltZXN0YW1wIHN0YXJ0YCksDQogICAgZGF0ZXRpbWVfZW5kICAgPSB5bWRfaG1zKGBUaW1lc3RhbXAgZW5kYCkNCiAgKQ0KDQpkYXRhX3BhdGllbnQgPC0gZGF0YV9yYXcgJT4lDQogIGdyb3VwX2J5KElEKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGFycml2YWxfdGltZSAgID0gbWluKGRhdGV0aW1lX2JlZ2luLCBuYS5ybSA9IFRSVUUpLA0KICAgIGRlcGFydHVyZV90aW1lID0gbWF4KGRhdGV0aW1lX2VuZCwgbmEucm0gPSBUUlVFKSwNCiAgICBwcmlvcml0YWlyZSA9IGlmZWxzZShhbnkoZ3JlcGwoIlBSSU8iLCBBY3Rpdml0eV9ERVRBSUxTKSksICJPdWkiLCAiTm9uIiksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApICU+JQ0KICBtdXRhdGUoDQogICAgZHVyZWVfc2Vqb3VyID0gYXMubnVtZXJpYygNCiAgICAgIGRpZmZ0aW1lKGRlcGFydHVyZV90aW1lLCBhcnJpdmFsX3RpbWUsIHVuaXRzID0gImhvdXJzIikNCiAgICApDQogICkNCg0KIyBOZXR0b3lhZ2UNCmRhdGFfcGF0aWVudCA8LSBkYXRhX3BhdGllbnQgJT4lDQogIGZpbHRlcighaXMubmEoZHVyZWVfc2Vqb3VyKSwgZHVyZWVfc2Vqb3VyID4gMCkNCg0KZHVyZWUgPC0gZGF0YV9wYXRpZW50JGR1cmVlX3Nlam91cg0KDQpmaXRfbm9ybSAgPC0gZml0ZGlzdHIoZHVyZWUsICJub3JtYWwiKQ0KZml0X2V4cCAgIDwtIGZpdGRpc3RyKGR1cmVlLCAiZXhwb25lbnRpYWwiKQ0KZml0X2dhbW1hIDwtIGZpdGRpc3RyKGR1cmVlLCAiZ2FtbWEiKQ0KZml0X3dlaWIgIDwtIGZpdGRpc3RyKGR1cmVlLCAid2VpYnVsbCIpDQoNCiMgLS0tIENhbGN1bCBtYW51ZWwgZGUgbOKAmUFJQw0KQUlDX2ZpdGRpc3RyIDwtIGZ1bmN0aW9uKGZpdCkgew0KICBrIDwtIGxlbmd0aChmaXQkZXN0aW1hdGUpDQogIC0yICogZml0JGxvZ2xpayArIDIgKiBrDQp9DQoNCkFJQ192YWx1ZXMgPC0gZGF0YS5mcmFtZSgNCiAgRGlzdHJpYnV0aW9uID0gYygiTm9ybWFsZSIsICJFeHBvbmVudGllbGxlIiwgIkdhbW1hIiwgIldlaWJ1bGwiKSwNCiAgQUlDID0gYygNCiAgICBBSUNfZml0ZGlzdHIoZml0X25vcm0pLA0KICAgIEFJQ19maXRkaXN0cihmaXRfZXhwKSwNCiAgICBBSUNfZml0ZGlzdHIoZml0X2dhbW1hKSwNCiAgICBBSUNfZml0ZGlzdHIoZml0X3dlaWIpDQogICkNCikNCg0KQUlDX3ZhbHVlcyA8LSBBSUNfdmFsdWVzW29yZGVyKEFJQ192YWx1ZXMkQUlDKSwgXQ0KcHJpbnQoQUlDX3ZhbHVlcykNCg0KYnJlYWtzXzMwIDwtIHNlcSgwLCBjZWlsaW5nKG1heChkdXJlZSkpLCBieSA9IDAuNSkNCg0KaGF6YXJkX3Bhcl90cmFuY2hlIDwtIGZ1bmN0aW9uKGR1cmVlcywgYnJlYWtzKSB7DQoNCiAgbiA8LSBsZW5ndGgoYnJlYWtzKSAtIDENCiAgaGF6YXJkIDwtIG51bWVyaWMobikNCg0KICBmb3IgKGkgaW4gMTpuKSB7DQoNCiAgICBkZWJ1dCA8LSBicmVha3NbaV0NCiAgICBmaW4gICA8LSBicmVha3NbaSArIDFdDQoNCiAgICBhX3Jpc3F1ZSA8LSBzdW0oZHVyZWVzID49IGRlYnV0KQ0KICAgIHNvcnRpZXMgIDwtIHN1bShkdXJlZXMgPj0gZGVidXQgJiBkdXJlZXMgPCBmaW4pDQoNCiAgICBoYXphcmRbaV0gPC0gaWZlbHNlKGFfcmlzcXVlID4gMCwgc29ydGllcyAvIGFfcmlzcXVlLCBOQSkNCiAgfQ0KDQogIGRhdGEuZnJhbWUoDQogICAgZGVidXQgPSBicmVha3NbLWxlbmd0aChicmVha3MpXSwNCiAgICBmaW4gICA9IGJyZWFrc1stMV0sDQogICAgaGF6YXJkID0gaGF6YXJkDQogICkNCn0NCg0KaGF6YXJkX2dsb2JhbCA8LSBoYXphcmRfcGFyX3RyYW5jaGUoZHVyZWUsIGJyZWFrc18zMCkNCg0KZGF0YV9wYXRpZW50IDwtIGRhdGFfcGF0aWVudCAlPiUNCiAgbXV0YXRlKA0KICAgIGhldXJlX2Fycml2ZWUgPSBob3VyKGFycml2YWxfdGltZSksDQogICAgYmxvY18yaCA9IGN1dCgNCiAgICAgIGhldXJlX2Fycml2ZWUsDQogICAgICBicmVha3MgPSBjKDgsIDEwLCAxMiwgMTQsIDE2LCAxOCksDQogICAgICByaWdodCA9IEZBTFNFLA0KICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFDQogICAgKQ0KICApDQoNCmhhemFyZF9wYXJfYmxvYyA8LSBkYXRhX3BhdGllbnQgJT4lDQogIGdyb3VwX2J5KGJsb2NfMmgpICU+JQ0KICBncm91cF9tYXAofiBoYXphcmRfcGFyX3RyYW5jaGUoLngkZHVyZWVfc2Vqb3VyLCBicmVha3NfMzApLA0KICAgICAgICAgICAgLmtlZXAgPSBUUlVFKQ0KDQpoYXphcmRfcHJpb3JpdGUgPC0gZGF0YV9wYXRpZW50ICU+JQ0KICBncm91cF9ieShwcmlvcml0YWlyZSkgJT4lDQogIGdyb3VwX21hcCh+IGhhemFyZF9wYXJfdHJhbmNoZSgueCRkdXJlZV9zZWpvdXIsIGJyZWFrc18zMCksDQogICAgICAgICAgICAua2VlcCA9IFRSVUUpDQoNCmhlYWQoaGF6YXJkX2dsb2JhbCkNCmhhemFyZF9wYXJfYmxvYw0KaGF6YXJkX3ByaW9yaXRlDQoNCg0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpnZ3Bsb3QoaGF6YXJkX2dsb2JhbCwgYWVzKHggPSBkZWJ1dCwgeSA9IGhhemFyZCkpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVGF1eCBkZSByaXNxdWUgZGUgc29ydGllIOKAkyBHbG9iYWwiLA0KICAgIHggPSAiVGVtcHMgZGUgc8Opam91ciAoaGV1cmVzKSIsDQogICAgeSA9ICJUYXV4IGRlIHJpc3F1ZSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpoYXphcmRfYmxvY19kZiA8LSBiaW5kX3Jvd3MoDQogIGhhemFyZF9wYXJfYmxvYywNCiAgLmlkID0gImJsb2MiDQopDQoNCiMgUsOpY3Vww6lyZXIgbGVzIHZyYWlzIG5vbXMgZGVzIGJsb2NzDQpoYXphcmRfYmxvY19kZiRibG9jIDwtIGxldmVscyhkYXRhX3BhdGllbnQkYmxvY18yaClbYXMubnVtZXJpYyhoYXphcmRfYmxvY19kZiRibG9jKV0NCg0KZ2dwbG90KGhhemFyZF9ibG9jX2RmLCBhZXMoeCA9IGRlYnV0LCB5ID0gaGF6YXJkLCBjb2xvciA9IGJsb2MpKSArDQogIGdlb21fbGluZShzaXplID0gMSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlRhdXggZGUgcmlzcXVlIHBhciBibG9jIGTigJlhcnJpdsOpZSIsDQogICAgeCA9ICJUZW1wcyBkZSBzw6lqb3VyIChoZXVyZXMpIiwNCiAgICB5ID0gIlRhdXggZGUgcmlzcXVlIiwNCiAgICBjb2xvciA9ICJCbG9jIGTigJlhcnJpdsOpZSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpoYXphcmRfcHJpb3JfZGYgPC0gYmluZF9yb3dzKA0KICBoYXphcmRfcHJpb3JpdGUsDQogIC5pZCA9ICJwcmlvcml0YWlyZSINCikNCg0KZ2dwbG90KGhhemFyZF9wcmlvcl9kZiwgYWVzKHggPSBkZWJ1dCwgeSA9IGhhemFyZCwgY29sb3IgPSBwcmlvcml0YWlyZSkpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVGF1eCBkZSByaXNxdWUg4oCTIFBhdGllbnRzIHByaW9yaXRhaXJlcyB2cyBub24gcHJpb3JpdGFpcmVzIiwNCiAgICB4ID0gIlRlbXBzIGRlIHPDqWpvdXIgKGhldXJlcykiLA0KICAgIHkgPSAiVGF1eCBkZSByaXNxdWUiLA0KICAgIGNvbG9yID0gIlByaW9yaXRhaXJlIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQojIyMgUXVlc3Rpb24gNikgQ2FsY3VscyBkZXMgbml2ZWF1eCBkJ29jY3VwYXRpb24gZXQgdmFsaWRhdGlvbiBkZXMgbW9kw6hsZXMgKC81KQ0KDQpBIHBhcnRpciBkZXMgbW9kw6hsZXMgcHLDqWPDqWRhbnQsIGlsIHZvdXMgZXN0IGRlbWFuZMOpIGQnZXN0aW1lciwgZGUNCnZpc3VhbGlzZXIgZXQgZCdhbmFseXNlciBsZSBuaXZlYXUgZCdvY2N1cGF0aW9uIGF1IGNvdXJzIGRlIGxhIGpvdXJuw6llDQplbiB1dGlsaXNhbnQgMzAgw6ljaGFudGlsbG9ucyBkZSBwcm9jZXNzdXMgZCdhcnJpdsOpZSBkZSBwYXRpZW50ICgzMA0Kam91cm7DqWVzIGV0IHBhcyAzMCBwYXRpZW50cykgSWwgdm91cyBlc3QgZW5zdWl0ZSBkZW1hbmTDqSBkZSBjb21wYXJlciBsYQ0KcXVhbGl0w6kgZGVzIGVzdGltYXRpb25zIGRlcyBkaWZmw6lyZW50cyBtb2TDqGxlcyDDoCBsYSByw6lhbGl0w6kgZGUgbWFuacOocmUNCnZpc3VlbGxlIGV0IGVuIHV0aWxpc2FudCB1bmUgbWVzdXJlIGQnZXJyZXVyIGFic29sdSBtb3llbm5lIGV0L291DQpxdWFkcmF0aXF1ZSAob3UgbWlzIMOgIGxhICRcc3FydCgpJCkgZXQgdW5lIG1lc3VyZSBkZSBiaWFpcyBzdXINCmwnb2NjdXBhdGlvbiBtb3llbm5lLg0KDQpSw6lwb25zZSA6DQoNCipFbnRyZXogdm90cmUgdGV4dGUgaWNpKg0KDQpgYGB7cn0NCiNfRW50cmV6IHZvdHJlIGNvZGUgUiBpY2lfDQoNCiMgRm9uY3Rpb24gcG91ciBjYWxjdWxlciBsJ29jY3VwYXRpb24gcsOpZWxsZQ0KY2FsY3VsZXJfb2NjdXBhdGlvbl9yZWVsbGUgPC0gZnVuY3Rpb24oZGF0YV9wYXRpZW50LCBkZWJ1dCwgZmluLCBwYXNfdGVtcHMgPSAwLjEpIHsNCiAgdGVtcHNfc2VxIDwtIHNlcShkZWJ1dCwgZmluLCBieSA9IHBhc190ZW1wcyAqIDM2MDApDQogIG9jY3VwYXRpb24gPC0gc2FwcGx5KHRlbXBzX3NlcSwgZnVuY3Rpb24odCkgew0KICAgIHN1bShkYXRhX3BhdGllbnQkYXJyaXZhbF90aW1lIDw9IHQgJiBkYXRhX3BhdGllbnQkZGVwYXJ0dXJlX3RpbWUgPj0gdCkNCiAgfSkNCiAgZGF0YS5mcmFtZSh0ZW1wcyA9IHRlbXBzX3NlcSwgb2NjdXBhdGlvbiA9IG9jY3VwYXRpb24pDQp9DQoNCiMgRMOpZmluaXIgbGEgcMOpcmlvZGUgZCdvYnNlcnZhdGlvbg0KZGF0ZV9qb3VyIDwtIGFzLkRhdGUoIjIwMTUtMTEtMTIiKQ0KZGVidXRfam91cm5lZSA8LSB5bWRfaG1zKHBhc3RlKGRhdGVfam91ciwgIjA4OjAwOjAwIikpDQpmaW5fam91cm5lZSA8LSB5bWRfaG1zKHBhc3RlKGRhdGVfam91ciwgIjE4OjAwOjAwIikpDQoNCiMgQ2FsY3VsZXIgbCdvY2N1cGF0aW9uIHLDqWVsbGUNCm9jY3VwYXRpb25fcmVlbGxlIDwtIGNhbGN1bGVyX29jY3VwYXRpb25fcmVlbGxlKGRhdGFfcGF0aWVudCwgZGVidXRfam91cm5lZSwgZmluX2pvdXJuZWUpDQoNCiMgVGF1eCBkJ2Fycml2w6llIHBhciBibG9jIGRlIDJoDQpkYXRhX3BhdGllbnQgPC0gZGF0YV9wYXRpZW50ICU+JQ0KICBtdXRhdGUoDQogICAgaGV1cmVfYXJyaXZlZSA9IGhvdXIoYXJyaXZhbF90aW1lKSwNCiAgICBibG9jXzJoID0gY3V0KGhldXJlX2Fycml2ZWUsIGJyZWFrcyA9IGMoOCwgMTAsIDEyLCAxNCwgMTYsIDE4KSwNCiAgICAgICAgICAgICAgICAgIHJpZ2h0ID0gRkFMU0UsIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkNCiAgKQ0KDQpsYW1iZGFfcGFyX2Jsb2MgPC0gZGF0YV9wYXRpZW50ICU+JQ0KICBncm91cF9ieShibG9jXzJoKSAlPiUNCiAgc3VtbWFyaXNlKG5iX2Fycml2ZWVzID0gbigpLCBsYW1iZGEgPSBuYl9hcnJpdmVlcyAvIDIpDQoNCiMgUGFyYW3DqHRyZXMgZGVzIGxvaXMgZGUgZHVyw6llDQpkdXJlZSA8LSBkYXRhX3BhdGllbnQkZHVyZWVfc2Vqb3VyDQpmaXRfZXhwIDwtIGZpdGRpc3RyKGR1cmVlLCAiZXhwb25lbnRpYWwiKQ0KZml0X2dhbW1hIDwtIGZpdGRpc3RyKGR1cmVlLCAiZ2FtbWEiKQ0KZml0X3dlaWIgPC0gZml0ZGlzdHIoZHVyZWUsICJ3ZWlidWxsIikNCg0KIyBGb25jdGlvbiBwb3VyIHNpbXVsZXIgdW5lIGpvdXJuw6llIGNvbXBsw6h0ZSAoYXJyaXbDqWVzICsgZHVyw6llcykNCnNpbXVsZXJfam91cm5lZSA8LSBmdW5jdGlvbihsYW1iZGFfcGFyX2Jsb2MsIGxvaV9kdXJlZSwgcGFyYW1zX2R1cmVlKSB7DQogIHBhdGllbnRzIDwtIGRhdGEuZnJhbWUoKQ0KICANCiAgZm9yIChpIGluIDE6bnJvdyhsYW1iZGFfcGFyX2Jsb2MpKSB7DQogICAgbGFtYmRhIDwtIGxhbWJkYV9wYXJfYmxvYyRsYW1iZGFbaV0NCiAgICBkZWJ1dF9ibG9jIDwtIGMoOCwgMTAsIDEyLCAxNCwgMTYpW2ldDQogICAgZmluX2Jsb2MgPC0gYygxMCwgMTIsIDE0LCAxNiwgMTgpW2ldDQogICAgDQogICAgIyBOb21icmUgZCdhcnJpdsOpZXMgKFBvaXNzb24pDQogICAgbmJfYXJyaXZlZXMgPC0gcnBvaXMoMSwgbGFtYmRhICogMikNCiAgICANCiAgICBpZiAobmJfYXJyaXZlZXMgPiAwKSB7DQogICAgICAjIFRlbXBzIGQnYXJyaXbDqWUgdW5pZm9ybWVzIGRhbnMgbGUgYmxvYw0KICAgICAgYXJyaXZlZXMgPC0gcnVuaWYobmJfYXJyaXZlZXMsIG1pbiA9IGRlYnV0X2Jsb2MsIG1heCA9IGZpbl9ibG9jKQ0KICAgICAgDQogICAgICAjIER1csOpZXMgc2Vsb24gbGEgbG9pIHNww6ljaWZpw6llDQogICAgICBpZiAobG9pX2R1cmVlID09ICJleHAiKSB7DQogICAgICAgIGR1cmVlcyA8LSByZXhwKG5iX2Fycml2ZWVzLCByYXRlID0gMS9wYXJhbXNfZHVyZWUkbXUpDQogICAgICB9IGVsc2UgaWYgKGxvaV9kdXJlZSA9PSAiZ2FtbWEiKSB7DQogICAgICAgIGR1cmVlcyA8LSByZ2FtbWEobmJfYXJyaXZlZXMsIHNoYXBlID0gcGFyYW1zX2R1cmVlJHNoYXBlLCByYXRlID0gcGFyYW1zX2R1cmVlJHJhdGUpDQogICAgICB9IGVsc2UgaWYgKGxvaV9kdXJlZSA9PSAid2VpYnVsbCIpIHsNCiAgICAgICAgZHVyZWVzIDwtIHJ3ZWlidWxsKG5iX2Fycml2ZWVzLCBzaGFwZSA9IHBhcmFtc19kdXJlZSRzaGFwZSwgc2NhbGUgPSBwYXJhbXNfZHVyZWUkc2NhbGUpDQogICAgICB9DQogICAgICANCiAgICAgIHBhdGllbnRzIDwtIHJiaW5kKHBhdGllbnRzLCBkYXRhLmZyYW1lKA0KICAgICAgICBhcnJpdmVlID0gYXJyaXZlZXMsIGR1cmVlID0gZHVyZWVzLCBkZXBhcnQgPSBhcnJpdmVlcyArIGR1cmVlcw0KICAgICAgKSkNCiAgICB9DQogIH0NCiAgcmV0dXJuKHBhdGllbnRzKQ0KfQ0KDQojIEZvbmN0aW9uIHBvdXIgY2FsY3VsZXIgbCdvY2N1cGF0aW9uIGQndW5lIHNpbXVsYXRpb24NCmNhbGN1bGVyX29jY3VwYXRpb25fc2ltdWxhdGlvbiA8LSBmdW5jdGlvbihwYXRpZW50c19zaW0sIGRlYnV0ID0gOCwgZmluID0gMTgsIHBhcyA9IDAuMSkgew0KICB0ZW1wc19zZXEgPC0gc2VxKGRlYnV0LCBmaW4sIGJ5ID0gcGFzKQ0KICBvY2N1cGF0aW9uIDwtIHNhcHBseSh0ZW1wc19zZXEsIGZ1bmN0aW9uKHQpIHsNCiAgICBzdW0ocGF0aWVudHNfc2ltJGFycml2ZWUgPD0gdCAmIHBhdGllbnRzX3NpbSRkZXBhcnQgPj0gdCkNCiAgfSkNCiAgZGF0YS5mcmFtZSh0ZW1wcyA9IHRlbXBzX3NlcSwgb2NjdXBhdGlvbiA9IG9jY3VwYXRpb24pDQp9DQoNCiMgTGFuY2VyIGxlcyAzMCBzaW11bGF0aW9ucyBwb3VyIGNoYXF1ZSBtb2TDqGxlDQpzZXQuc2VlZCgxMjMpDQpuX3NpbXVsYXRpb25zIDwtIDMwDQoNCnJlc3VsdGF0c19leHAgPC0gbGFwcGx5KDE6bl9zaW11bGF0aW9ucywgZnVuY3Rpb24oaSkgew0KICBjYWxjdWxlcl9vY2N1cGF0aW9uX3NpbXVsYXRpb24oDQogICAgc2ltdWxlcl9qb3VybmVlKGxhbWJkYV9wYXJfYmxvYywgImV4cCIsIGxpc3QobXUgPSAxL2ZpdF9leHAkZXN0aW1hdGVbInJhdGUiXSkpDQogICkNCn0pDQoNCnJlc3VsdGF0c19nYW1tYSA8LSBsYXBwbHkoMTpuX3NpbXVsYXRpb25zLCBmdW5jdGlvbihpKSB7DQogIGNhbGN1bGVyX29jY3VwYXRpb25fc2ltdWxhdGlvbigNCiAgICBzaW11bGVyX2pvdXJuZWUobGFtYmRhX3Bhcl9ibG9jLCAiZ2FtbWEiLCANCiAgICAgICAgICAgICAgICAgICBsaXN0KHNoYXBlID0gZml0X2dhbW1hJGVzdGltYXRlWyJzaGFwZSJdLCByYXRlID0gZml0X2dhbW1hJGVzdGltYXRlWyJyYXRlIl0pKQ0KICApDQp9KQ0KDQpyZXN1bHRhdHNfd2VpYiA8LSBsYXBwbHkoMTpuX3NpbXVsYXRpb25zLCBmdW5jdGlvbihpKSB7DQogIGNhbGN1bGVyX29jY3VwYXRpb25fc2ltdWxhdGlvbigNCiAgICBzaW11bGVyX2pvdXJuZWUobGFtYmRhX3Bhcl9ibG9jLCAid2VpYnVsbCIsIA0KICAgICAgICAgICAgICAgICAgIGxpc3Qoc2hhcGUgPSBmaXRfd2VpYiRlc3RpbWF0ZVsic2hhcGUiXSwgc2NhbGUgPSBmaXRfd2VpYiRlc3RpbWF0ZVsic2NhbGUiXSkpDQogICkNCn0pDQoNCiMgQWdyw6lnYXRpb24gZGVzIHLDqXN1bHRhdHMgKG1veWVubmUgZXQgSUMgOTAlKQ0KYWdyZWdlcl9zaW11bGF0aW9ucyA8LSBmdW5jdGlvbihsaXN0ZV9yZXN1bHRhdHMpIHsNCiAgdGVtcHMgPC0gbGlzdGVfcmVzdWx0YXRzW1sxXV0kdGVtcHMNCiAgbWF0X29jY3VwYXRpb24gPC0gc2FwcGx5KGxpc3RlX3Jlc3VsdGF0cywgZnVuY3Rpb24oeCkgeCRvY2N1cGF0aW9uKQ0KICBkYXRhLmZyYW1lKA0KICAgIHRlbXBzID0gdGVtcHMsDQogICAgb2NjdXBhdGlvbl9tb3kgPSByb3dNZWFucyhtYXRfb2NjdXBhdGlvbiksDQogICAgb2NjdXBhdGlvbl9xMDUgPSBhcHBseShtYXRfb2NjdXBhdGlvbiwgMSwgcXVhbnRpbGUsIHByb2JzID0gMC4wNSksDQogICAgb2NjdXBhdGlvbl9xOTUgPSBhcHBseShtYXRfb2NjdXBhdGlvbiwgMSwgcXVhbnRpbGUsIHByb2JzID0gMC45NSkNCiAgKQ0KfQ0KDQpvY2N1cGF0aW9uX2V4cF9hZ2cgPC0gYWdyZWdlcl9zaW11bGF0aW9ucyhyZXN1bHRhdHNfZXhwKQ0Kb2NjdXBhdGlvbl9nYW1tYV9hZ2cgPC0gYWdyZWdlcl9zaW11bGF0aW9ucyhyZXN1bHRhdHNfZ2FtbWEpDQpvY2N1cGF0aW9uX3dlaWJfYWdnIDwtIGFncmVnZXJfc2ltdWxhdGlvbnMocmVzdWx0YXRzX3dlaWIpDQoNCm9jY3VwYXRpb25fcmVlbGxlX3Bsb3QgPC0gb2NjdXBhdGlvbl9yZWVsbGUgJT4lDQogIG11dGF0ZSh0ZW1wc19oZXVyZSA9IGFzLm51bWVyaWMoZGlmZnRpbWUodGVtcHMsIGRlYnV0X2pvdXJuZWUsIHVuaXRzID0gImhvdXJzIikpICsgOCkNCg0KZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoZGF0YSA9IG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QsIGFlcyh4ID0gdGVtcHNfaGV1cmUsIHkgPSBvY2N1cGF0aW9uKSwgDQogICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDEuMikgKw0KICBnZW9tX2xpbmUoZGF0YSA9IG9jY3VwYXRpb25fZXhwX2FnZywgYWVzKHggPSB0ZW1wcywgeSA9IG9jY3VwYXRpb25fbW95KSwgDQogICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ld2lkdGggPSAwLjgsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZ2VvbV9yaWJib24oZGF0YSA9IG9jY3VwYXRpb25fZXhwX2FnZywgYWVzKHggPSB0ZW1wcywgeW1pbiA9IG9jY3VwYXRpb25fcTA1LCB5bWF4ID0gb2NjdXBhdGlvbl9xOTUpLA0KICAgICAgICAgICAgICBmaWxsID0gInJlZCIsIGFscGhhID0gMC4xKSArDQogIGdlb21fbGluZShkYXRhID0gb2NjdXBhdGlvbl9nYW1tYV9hZ2csIGFlcyh4ID0gdGVtcHMsIHkgPSBvY2N1cGF0aW9uX21veSksIA0KICAgICAgICAgICAgY29sb3IgPSAiYmx1ZSIsIGxpbmV3aWR0aCA9IDAuOCwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBnZW9tX3JpYmJvbihkYXRhID0gb2NjdXBhdGlvbl9nYW1tYV9hZ2csIGFlcyh4ID0gdGVtcHMsIHltaW4gPSBvY2N1cGF0aW9uX3EwNSwgeW1heCA9IG9jY3VwYXRpb25fcTk1KSwNCiAgICAgICAgICAgICAgZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjEpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBvY2N1cGF0aW9uX3dlaWJfYWdnLCBhZXMoeCA9IHRlbXBzLCB5ID0gb2NjdXBhdGlvbl9tb3kpLCANCiAgICAgICAgICAgIGNvbG9yID0gImdyZWVuIiwgbGluZXdpZHRoID0gMC44LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGdlb21fcmliYm9uKGRhdGEgPSBvY2N1cGF0aW9uX3dlaWJfYWdnLCBhZXMoeCA9IHRlbXBzLCB5bWluID0gb2NjdXBhdGlvbl9xMDUsIHltYXggPSBvY2N1cGF0aW9uX3E5NSksDQogICAgICAgICAgICAgIGZpbGwgPSAiZ3JlZW4iLCBhbHBoYSA9IDAuMSkgKw0KICBsYWJzKHRpdGxlID0gIk9jY3VwYXRpb24gOiBSw6llbCB2cyBNb2TDqGxlcyBzaW11bMOpcyAoMzAgam91cm7DqWVzLCBJQyA5MCUpIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJOb2lyID0gUsOpZWwgfCBSb3VnZSA9IEV4cCB8IEJsZXUgPSBHYW1tYSB8IFZlcnQgPSBXZWlidWxsIiwNCiAgICAgICB4ID0gIkhldXJlIiwgeSA9ICJOb21icmUgZGUgcGF0aWVudHMiKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoOCwgMTgsIGJ5ID0gMikpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmNhbGN1bGVyX21ldHJpcXVlcyA8LSBmdW5jdGlvbihvY2N1cGF0aW9uX3NpbV9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpIHsNCiAgb2NjdXBhdGlvbl9yZWVsbGVfaW50ZXJwIDwtIGFwcHJveCgNCiAgICB4ID0gb2NjdXBhdGlvbl9yZWVsbGVfcGxvdCR0ZW1wc19oZXVyZSwNCiAgICB5ID0gb2NjdXBhdGlvbl9yZWVsbGVfcGxvdCRvY2N1cGF0aW9uLA0KICAgIHhvdXQgPSBvY2N1cGF0aW9uX3NpbV9hZ2ckdGVtcHMNCiAgKSR5DQogIA0KICBtYWUgPC0gbWVhbihhYnMob2NjdXBhdGlvbl9zaW1fYWdnJG9jY3VwYXRpb25fbW95IC0gb2NjdXBhdGlvbl9yZWVsbGVfaW50ZXJwKSwgbmEucm0gPSBUUlVFKQ0KICBybXNlIDwtIHNxcnQobWVhbigob2NjdXBhdGlvbl9zaW1fYWdnJG9jY3VwYXRpb25fbW95IC0gb2NjdXBhdGlvbl9yZWVsbGVfaW50ZXJwKV4yLCBuYS5ybSA9IFRSVUUpKQ0KICBiaWFpcyA8LSBtZWFuKG9jY3VwYXRpb25fc2ltX2FnZyRvY2N1cGF0aW9uX21veSwgbmEucm0gPSBUUlVFKSAtIA0KICAgICAgICAgICBtZWFuKG9jY3VwYXRpb25fcmVlbGxlX2ludGVycCwgbmEucm0gPSBUUlVFKQ0KICANCiAgYyhNQUUgPSBtYWUsIFJNU0UgPSBybXNlLCBCaWFpcyA9IGJpYWlzKQ0KfQ0KDQp0YWJsZWF1X21ldHJpcXVlcyA8LSBkYXRhLmZyYW1lKA0KICBNb2TDqGxlID0gYygiRXhwb25lbnRpZWxsZSIsICJHYW1tYSIsICJXZWlidWxsIiksDQogIE1BRSA9IGMoY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fZXhwX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIk1BRSJdLA0KICAgICAgICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2dhbW1hX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIk1BRSJdLA0KICAgICAgICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX3dlaWJfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiTUFFIl0pLA0KICBSTVNFID0gYyhjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl9leHBfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiUk1TRSJdLA0KICAgICAgICAgICBjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl9nYW1tYV9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJSTVNFIl0sDQogICAgICAgICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX3dlaWJfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiUk1TRSJdKSwNCiAgQmlhaXMgPSBjKGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2V4cF9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJCaWFpcyJdLA0KICAgICAgICAgICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fZ2FtbWFfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiQmlhaXMiXSwNCiAgICAgICAgICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX3dlaWJfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiQmlhaXMiXSkNCikNCg0KcHJpbnQodGFibGVhdV9tZXRyaXF1ZXMsIHJvdy5uYW1lcyA9IEZBTFNFLCBkaWdpdHMgPSAzKQ0KYGBgDQoNClJlZmFpdGVzIGNlIHRyYXZhaWwgZCdlc3RpbWF0aW9uIGRlIGwnb2NjdXBhdGlvbiBlbiBjb25zaWTDqXJhbnQgbGVzDQphcnJpdsOpZXMgZGVzIHBhdGllbnRzIGNvbm51ZXMgZXQgdmlzdWFsaXNlciBldCBhbmFseXNlciBsZXMgZ2FpbnMgZW4NCnRlcm1lIGRlIHF1YWxpdMOpIGRlIHByw6lkaWN0aW9uLg0KDQpSw6lwb25zZSA6DQoNCipFbnRyZXogdm90cmUgdGV4dGUgaWNpKg0KDQpgYGB7cn0NCiNfRW50cmV6IHZvdHJlIGNvZGUgUiBpY2lfDQoNCiMgRm9uY3Rpb24gZGUgc2ltdWxhdGlvbiBhdmVjIGFycml2w6llcyBjb25udWVzDQpzaW11bGVyX2F2ZWNfYXJyaXZlZXNfY29ubnVlcyA8LSBmdW5jdGlvbihkYXRhX3BhdGllbnQsIGxvaV9kdXJlZSwgcGFyYW1zX2R1cmVlLCBuX3NpbSA9IDMwKSB7DQogICMgRXh0cmFpcmUgbGVzIGhldXJlcyBkJ2Fycml2w6llIHLDqWVsbGVzDQogIGFycml2ZWVzX3JlZWxsZXMgPC0gaG91cihkYXRhX3BhdGllbnQkYXJyaXZhbF90aW1lKSArIG1pbnV0ZShkYXRhX3BhdGllbnQkYXJyaXZhbF90aW1lKS82MA0KICBuX3BhdGllbnRzIDwtIG5yb3coZGF0YV9wYXRpZW50KQ0KICANCiAgIyBTaW11bGVyIG5fc2ltIGpvdXJuw6llcw0KICBsYXBwbHkoMTpuX3NpbSwgZnVuY3Rpb24oaSkgew0KICAgICMgR8OpbsOpcmVyIGxlcyBkdXLDqWVzIHNlbG9uIGxlIG1vZMOobGUNCiAgICBpZiAobG9pX2R1cmVlID09ICJleHAiKSB7DQogICAgICBkdXJlZXMgPC0gcmV4cChuX3BhdGllbnRzLCByYXRlID0gMS9wYXJhbXNfZHVyZWUkbXUpDQogICAgfSBlbHNlIGlmIChsb2lfZHVyZWUgPT0gImdhbW1hIikgew0KICAgICAgZHVyZWVzIDwtIHJnYW1tYShuX3BhdGllbnRzLCBzaGFwZSA9IHBhcmFtc19kdXJlZSRzaGFwZSwgcmF0ZSA9IHBhcmFtc19kdXJlZSRyYXRlKQ0KICAgIH0gZWxzZSBpZiAobG9pX2R1cmVlID09ICJ3ZWlidWxsIikgew0KICAgICAgZHVyZWVzIDwtIHJ3ZWlidWxsKG5fcGF0aWVudHMsIHNoYXBlID0gcGFyYW1zX2R1cmVlJHNoYXBlLCBzY2FsZSA9IHBhcmFtc19kdXJlZSRzY2FsZSkNCiAgICB9DQogICAgDQogICAgIyBDYWxjdWxlciBsJ29jY3VwYXRpb24NCiAgICBjYWxjdWxlcl9vY2N1cGF0aW9uX3NpbXVsYXRpb24oDQogICAgICBkYXRhLmZyYW1lKGFycml2ZWUgPSBhcnJpdmVlc19yZWVsbGVzLCBkdXJlZSA9IGR1cmVlcywgZGVwYXJ0ID0gYXJyaXZlZXNfcmVlbGxlcyArIGR1cmVlcykNCiAgICApDQogIH0pDQp9DQoNCiMgU2ltdWxhdGlvbnMgcG91ciBjaGFxdWUgbW9kw6hsZQ0Kc2V0LnNlZWQoNDU2KQ0KcmVzdWx0YXRzX2V4cF9jb25udWVzIDwtIHNpbXVsZXJfYXZlY19hcnJpdmVlc19jb25udWVzKA0KICBkYXRhX3BhdGllbnQsICJleHAiLCBsaXN0KG11ID0gMS9maXRfZXhwJGVzdGltYXRlWyJyYXRlIl0pLCBuX3NpbXVsYXRpb25zDQopDQoNCnJlc3VsdGF0c19nYW1tYV9jb25udWVzIDwtIHNpbXVsZXJfYXZlY19hcnJpdmVlc19jb25udWVzKA0KICBkYXRhX3BhdGllbnQsICJnYW1tYSIsIA0KICBsaXN0KHNoYXBlID0gZml0X2dhbW1hJGVzdGltYXRlWyJzaGFwZSJdLCByYXRlID0gZml0X2dhbW1hJGVzdGltYXRlWyJyYXRlIl0pLCANCiAgbl9zaW11bGF0aW9ucw0KKQ0KDQpyZXN1bHRhdHNfd2VpYl9jb25udWVzIDwtIHNpbXVsZXJfYXZlY19hcnJpdmVlc19jb25udWVzKA0KICBkYXRhX3BhdGllbnQsICJ3ZWlidWxsIiwgDQogIGxpc3Qoc2hhcGUgPSBmaXRfd2VpYiRlc3RpbWF0ZVsic2hhcGUiXSwgc2NhbGUgPSBmaXRfd2VpYiRlc3RpbWF0ZVsic2NhbGUiXSksIA0KICBuX3NpbXVsYXRpb25zDQopDQoNCiMgQWdyw6lnYXRpb24NCm9jY3VwYXRpb25fZXhwX2Nvbm51ZXNfYWdnIDwtIGFncmVnZXJfc2ltdWxhdGlvbnMocmVzdWx0YXRzX2V4cF9jb25udWVzKQ0Kb2NjdXBhdGlvbl9nYW1tYV9jb25udWVzX2FnZyA8LSBhZ3JlZ2VyX3NpbXVsYXRpb25zKHJlc3VsdGF0c19nYW1tYV9jb25udWVzKQ0Kb2NjdXBhdGlvbl93ZWliX2Nvbm51ZXNfYWdnIDwtIGFncmVnZXJfc2ltdWxhdGlvbnMocmVzdWx0YXRzX3dlaWJfY29ubnVlcykNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgVklTVUFMSVNBVElPTg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoZGF0YSA9IG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QsIGFlcyh4ID0gdGVtcHNfaGV1cmUsIHkgPSBvY2N1cGF0aW9uKSwgDQogICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDEuMikgKw0KICBnZW9tX2xpbmUoZGF0YSA9IG9jY3VwYXRpb25fZXhwX2Nvbm51ZXNfYWdnLCBhZXMoeCA9IHRlbXBzLCB5ID0gb2NjdXBhdGlvbl9tb3kpLCANCiAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV3aWR0aCA9IDAuOCwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBnZW9tX3JpYmJvbihkYXRhID0gb2NjdXBhdGlvbl9leHBfY29ubnVlc19hZ2csIA0KICAgICAgICAgICAgICBhZXMoeCA9IHRlbXBzLCB5bWluID0gb2NjdXBhdGlvbl9xMDUsIHltYXggPSBvY2N1cGF0aW9uX3E5NSksDQogICAgICAgICAgICAgIGZpbGwgPSAicmVkIiwgYWxwaGEgPSAwLjE1KSArDQogIGdlb21fbGluZShkYXRhID0gb2NjdXBhdGlvbl9nYW1tYV9jb25udWVzX2FnZywgYWVzKHggPSB0ZW1wcywgeSA9IG9jY3VwYXRpb25fbW95KSwgDQogICAgICAgICAgICBjb2xvciA9ICJibHVlIiwgbGluZXdpZHRoID0gMC44LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGdlb21fcmliYm9uKGRhdGEgPSBvY2N1cGF0aW9uX2dhbW1hX2Nvbm51ZXNfYWdnLCANCiAgICAgICAgICAgICAgYWVzKHggPSB0ZW1wcywgeW1pbiA9IG9jY3VwYXRpb25fcTA1LCB5bWF4ID0gb2NjdXBhdGlvbl9xOTUpLA0KICAgICAgICAgICAgICBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuMTUpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBvY2N1cGF0aW9uX3dlaWJfY29ubnVlc19hZ2csIGFlcyh4ID0gdGVtcHMsIHkgPSBvY2N1cGF0aW9uX21veSksIA0KICAgICAgICAgICAgY29sb3IgPSAiZ3JlZW4iLCBsaW5ld2lkdGggPSAwLjgsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZ2VvbV9yaWJib24oZGF0YSA9IG9jY3VwYXRpb25fd2VpYl9jb25udWVzX2FnZywgDQogICAgICAgICAgICAgIGFlcyh4ID0gdGVtcHMsIHltaW4gPSBvY2N1cGF0aW9uX3EwNSwgeW1heCA9IG9jY3VwYXRpb25fcTk1KSwNCiAgICAgICAgICAgICAgZmlsbCA9ICJncmVlbiIsIGFscGhhID0gMC4xNSkgKw0KICBsYWJzKHRpdGxlID0gIk9jY3VwYXRpb24gYXZlYyBBUlJJVsOJRVMgQ09OTlVFUyAoMzAgc2ltdWxhdGlvbnMsIElDIDkwJSkiLA0KICAgICAgIHN1YnRpdGxlID0gIk5vaXIgPSBSw6llbCB8IFJvdWdlID0gRXhwIHwgQmxldSA9IEdhbW1hIHwgVmVydCA9IFdlaWJ1bGwiLA0KICAgICAgIHggPSAiSGV1cmUiLCB5ID0gIk5vbWJyZSBkZSBwYXRpZW50cyIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSg4LCAxOCwgYnkgPSAyKSkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgTcOJVFJJUVVFUyBFVCBDT01QQVJBSVNPTg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KIyBNw6l0cmlxdWVzIGF2ZWMgYXJyaXbDqWVzIGNvbm51ZXMNCm1ldHJpcXVlc19leHBfY29ubnVlcyA8LSBjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl9leHBfY29ubnVlc19hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpDQptZXRyaXF1ZXNfZ2FtbWFfY29ubnVlcyA8LSBjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl9nYW1tYV9jb25udWVzX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdCkNCm1ldHJpcXVlc193ZWliX2Nvbm51ZXMgPC0gY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fd2VpYl9jb25udWVzX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdCkNCg0KIyBUYWJsZWF1IGNvbXBhcmF0aWYgQVZBTlQvQVBSw4hTDQpjb21wYXJhaXNvbiA8LSBkYXRhLmZyYW1lKA0KICBNb2TDqGxlID0gcmVwKGMoIkV4cG9uZW50aWVsbGUiLCAiR2FtbWEiLCAiV2VpYnVsbCIpLCAyKSwNCiAgQXBwcm9jaGUgPSBjKHJlcCgiQXJyaXbDqWVzIHNpbXVsw6llcyIsIDMpLCByZXAoIkFycml2w6llcyBjb25udWVzIiwgMykpLA0KICBNQUUgPSBjKA0KICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2V4cF9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJNQUUiXSwNCiAgICBjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl9nYW1tYV9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJNQUUiXSwNCiAgICBjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl93ZWliX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIk1BRSJdLA0KICAgIG1ldHJpcXVlc19leHBfY29ubnVlc1siTUFFIl0sDQogICAgbWV0cmlxdWVzX2dhbW1hX2Nvbm51ZXNbIk1BRSJdLA0KICAgIG1ldHJpcXVlc193ZWliX2Nvbm51ZXNbIk1BRSJdDQogICksDQogIFJNU0UgPSBjKA0KICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2V4cF9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJSTVNFIl0sDQogICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fZ2FtbWFfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiUk1TRSJdLA0KICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX3dlaWJfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiUk1TRSJdLA0KICAgIG1ldHJpcXVlc19leHBfY29ubnVlc1siUk1TRSJdLA0KICAgIG1ldHJpcXVlc19nYW1tYV9jb25udWVzWyJSTVNFIl0sDQogICAgbWV0cmlxdWVzX3dlaWJfY29ubnVlc1siUk1TRSJdDQogICkNCikNCg0KcHJpbnQoY29tcGFyYWlzb24sIHJvdy5uYW1lcyA9IEZBTFNFLCBkaWdpdHMgPSAzKQ0KDQojIENhbGN1bCBkZXMgZ2FpbnMNCmdhaW5zIDwtIGRhdGEuZnJhbWUoDQogIE1vZMOobGUgPSBjKCJFeHBvbmVudGllbGxlIiwgIkdhbW1hIiwgIldlaWJ1bGwiKSwNCiAgUmVkdWN0aW9uX01BRV9wY3QgPSBjKA0KICAgIChjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl9leHBfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiTUFFIl0gLSANCiAgICAgbWV0cmlxdWVzX2V4cF9jb25udWVzWyJNQUUiXSkgLyANCiAgICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fZXhwX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIk1BRSJdICogMTAwLA0KICAgIA0KICAgIChjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl9nYW1tYV9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJNQUUiXSAtIA0KICAgICBtZXRyaXF1ZXNfZ2FtbWFfY29ubnVlc1siTUFFIl0pIC8gDQogICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2dhbW1hX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIk1BRSJdICogMTAwLA0KICAgIA0KICAgIChjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl93ZWliX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIk1BRSJdIC0gDQogICAgIG1ldHJpcXVlc193ZWliX2Nvbm51ZXNbIk1BRSJdKSAvIA0KICAgICBjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl93ZWliX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIk1BRSJdICogMTAwDQogICksDQogIFJlZHVjdGlvbl9STVNFX3BjdCA9IGMoDQogICAgKGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2V4cF9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJSTVNFIl0gLSANCiAgICAgbWV0cmlxdWVzX2V4cF9jb25udWVzWyJSTVNFIl0pIC8gDQogICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2V4cF9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJSTVNFIl0gKiAxMDAsDQogICAgDQogICAgKGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2dhbW1hX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIlJNU0UiXSAtIA0KICAgICBtZXRyaXF1ZXNfZ2FtbWFfY29ubnVlc1siUk1TRSJdKSAvIA0KICAgICBjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl9nYW1tYV9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJSTVNFIl0gKiAxMDAsDQogICAgDQogICAgKGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX3dlaWJfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiUk1TRSJdIC0gDQogICAgIG1ldHJpcXVlc193ZWliX2Nvbm51ZXNbIlJNU0UiXSkgLyANCiAgICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fd2VpYl9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJSTVNFIl0gKiAxMDANCiAgKQ0KKQ0KDQpjYXQoIlxuPT09IEdBSU5TIEVOIFFVQUxJVMOJIERFIFBSw4lESUNUSU9OID09PVxuIikNCnByaW50KGdhaW5zLCByb3cubmFtZXMgPSBGQUxTRSwgZGlnaXRzID0gMSkNCmBgYA0KDQpgYGAgICAgICAgICANCk5vdGVzIDogSWNpLCBvbiBuZSBzw6lwYXJlIHBhcyBsZXMgZG9ubsOpZXMgZW4gZG9ubsOpZXMgZCdlbnRyYcOubmVtZW50IGV0IGRlIHRlc3QgcG91ciBkZXMgcmFpc29ucyBwcmF0aXF1ZXMgZGUgcXVhbnRpdMOpIGRlIGRvbm7DqWVzIG1haXMgYydlc3QgdW4gcG9pbnQgw6AgdGVuaXIgZW4gY29tcHRlIGRhbnMgdW5lIHZyYWkgdmFsaWRhdGlvbiBkZSBtb2TDqGxlLg0KYGBgDQoNCiMjIyBRdWVzdGlvbiBib251cyAoLzEpIDoNCg0KUXVlbGxlcyBzb250IGxlcyBub3RhdGlvbnMgZGUgS2VuZGFsbCBkZXMgZGlmZsOpcmVudHMgbW9kw6hsZXMgZGUgImZpbGUNCmQnYXR0ZW50ZSIgcXVlIHZvdXMgYXZleiBpbXBsw6ltZW50w6lzID8gKEp1c3RpZmlleikNCg0KYGBgICAgICAgICAgDQpOb3RlIDogIE9uIGVzdCBpY2kgc3VyIHVuIGNhcyBzcMOpY2lhbCBsZSBtb2TDqGxlIGRlIGZpbGUgZCdhdHRlbnRlIGVzdCBzYW5zIGZpbGUgZCdhdHRlbnRlDQpgYGANCg==